From 71a58e5ab243df311401ccb10e12154362beecde Mon Sep 17 00:00:00 2001 From: penghuo Date: Tue, 25 Aug 2020 17:03:35 -0700 Subject: [PATCH 1/4] add serialization support for expression --- .../data/model/AbstractExprNumberValue.java | 5 + .../sql/data/model/ExprShortValue.java | 56 ++ .../sql/data/model/ExprValue.java | 8 + .../sql/data/type/ExprCoreType.java | 7 +- .../sql/expression/DSL.java | 5 + .../sql/expression/function/FunctionDSL.java | 99 ++- .../function/SerializableNoArgFunction.java | 27 + .../function/SerializableTriFunction.java | 40 + .../expression/operator/OperatorUtils.java | 294 ------- .../arthmetic/ArithmeticFunction.java | 194 +++-- .../arthmetic/MathematicalFunction.java | 742 ++++++++---------- .../sql/data/model/ExprNumberValueTest.java | 34 + .../aggregation/AvgAggregatorTest.java | 2 +- .../aggregation/SumAggregatorTest.java | 2 +- .../arthmetic/ArithmeticFunctionTest.java | 65 +- .../arthmetic/MathematicalFunctionTest.java | 54 ++ .../BinaryPredicateOperatorTest.java | 30 +- .../sql/utils/ComparisonUtil.java | 5 +- 18 files changed, 881 insertions(+), 788 deletions(-) create mode 100644 core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprShortValue.java create mode 100644 core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/SerializableNoArgFunction.java create mode 100644 core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/SerializableTriFunction.java delete mode 100644 core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/OperatorUtils.java create mode 100644 core/src/test/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprNumberValueTest.java diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/model/AbstractExprNumberValue.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/model/AbstractExprNumberValue.java index fdf68b9ad8..8a2ff1d523 100644 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/model/AbstractExprNumberValue.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/model/AbstractExprNumberValue.java @@ -32,6 +32,11 @@ public boolean isNumber() { return true; } + @Override + public Short shortValue() { + return value.shortValue(); + } + @Override public Integer integerValue() { return value.intValue(); diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprShortValue.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprShortValue.java new file mode 100644 index 0000000000..1f168e7ca2 --- /dev/null +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprShortValue.java @@ -0,0 +1,56 @@ +/* + * + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file 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.amazon.opendistroforelasticsearch.sql.data.model; + +import com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType; +import com.amazon.opendistroforelasticsearch.sql.data.type.ExprType; + +/** + * Expression Short Value. + */ +public class ExprShortValue extends AbstractExprNumberValue { + + public ExprShortValue(Number value) { + super(value); + } + + @Override + public Object value() { + return shortValue(); + } + + @Override + public ExprType type() { + return ExprCoreType.SHORT; + } + + @Override + public String toString() { + return shortValue().toString(); + } + + @Override + public int compare(ExprValue other) { + return Short.compare(shortValue(), other.shortValue()); + } + + @Override + public boolean equal(ExprValue other) { + return shortValue().equals(other.shortValue()); + } +} diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprValue.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprValue.java index bc319a08c5..56e0859384 100644 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprValue.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprValue.java @@ -75,6 +75,14 @@ default BindingTuple bindingTuples() { return BindingTuple.EMPTY; } + /** + * Get short value. + */ + default Short shortValue() { + throw new ExpressionEvaluationException( + "invalid to get shortValue from value of type " + type()); + } + /** * Get integer value. */ diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/type/ExprCoreType.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/type/ExprCoreType.java index 9090673813..6f9c9b43b2 100644 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/type/ExprCoreType.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/type/ExprCoreType.java @@ -17,6 +17,7 @@ package com.amazon.opendistroforelasticsearch.sql.data.type; +import com.google.common.collect.ImmutableList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @@ -91,10 +92,14 @@ public String typeName() { } /** - * Retrun all the valid ExprCoreType. + * Return all the valid ExprCoreType. */ public static List coreTypes() { return Arrays.stream(ExprCoreType.values()).filter(type -> type != UNKNOWN) .collect(Collectors.toList()); } + + public static List numberTypes() { + return ImmutableList.of(INTEGER, LONG, FLOAT, DOUBLE); + } } diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/DSL.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/DSL.java index 86f7978427..82787acecb 100644 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/DSL.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/DSL.java @@ -15,6 +15,7 @@ package com.amazon.opendistroforelasticsearch.sql.expression; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprShortValue; import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValue; import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils; import com.amazon.opendistroforelasticsearch.sql.data.type.ExprType; @@ -28,6 +29,10 @@ public class DSL { private final BuiltinFunctionRepository repository; + public static LiteralExpression literal(Short value) { + return new LiteralExpression(new ExprShortValue(value)); + } + public static LiteralExpression literal(Integer value) { return new LiteralExpression(ExprValueUtils.integerValue(value)); } diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/FunctionDSL.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/FunctionDSL.java index 9d64d15bba..f3c13059b6 100644 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/FunctionDSL.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/FunctionDSL.java @@ -44,7 +44,7 @@ public class FunctionDSL { * @return FunctionResolver. */ public static FunctionResolver define(FunctionName functionName, - Function>... functions) { return define(functionName, Arrays.asList(functions)); } @@ -57,7 +57,7 @@ public static FunctionResolver define(FunctionName functionName, * @return FunctionResolver. */ public static FunctionResolver define(FunctionName functionName, - List>> functions) { FunctionResolver.FunctionResolverBuilder builder = FunctionResolver.builder(); @@ -69,6 +69,41 @@ public static FunctionResolver define(FunctionName functionName, return builder.build(); } + /** + * No Arg Function Implementation. + * + * @param function {@link ExprValue} based unary function. + * @param returnType return type. + * @return Unary Function Implementation. + */ + public static SerializableFunction> impl( + SerializableNoArgFunction function, + ExprType returnType) { + + return functionName -> { + FunctionSignature functionSignature = + new FunctionSignature(functionName, Collections.emptyList()); + FunctionBuilder functionBuilder = + arguments -> new FunctionExpression(functionName, Collections.emptyList()) { + @Override + public ExprValue valueOf(Environment valueEnv) { + return function.get(); + } + + @Override + public ExprType type() { + return returnType; + } + + @Override + public String toString() { + return String.format("%s()", functionName); + } + }; + return Pair.of(functionSignature, functionBuilder); + }; + } + /** * Unary Function Implementation. * @@ -152,6 +187,50 @@ public String toString() { }; } + /** + * Triple Function Implementation. + * + * @param function {@link ExprValue} based unary function. + * @param returnType return type. + * @param args1Type argument type. + * @param args2Type argument type. + * @return Binary Function Implementation. + */ + public static SerializableFunction> impl( + SerializableTriFunction function, + ExprType returnType, + ExprType args1Type, + ExprType args2Type, + ExprType args3Type) { + + return functionName -> { + FunctionSignature functionSignature = + new FunctionSignature(functionName, Arrays.asList(args1Type, args2Type, args3Type)); + FunctionBuilder functionBuilder = + arguments -> new FunctionExpression(functionName, arguments) { + @Override + public ExprValue valueOf(Environment valueEnv) { + ExprValue arg1 = arguments.get(0).valueOf(valueEnv); + ExprValue arg2 = arguments.get(1).valueOf(valueEnv); + ExprValue arg3 = arguments.get(2).valueOf(valueEnv); + return function.apply(arg1, arg2, arg3); + } + + @Override + public ExprType type() { + return returnType; + } + + @Override + public String toString() { + return String.format("%s(%s, %s, %s)", functionName, arguments.get(0).toString(), + arguments.get(1).toString(), arguments.get(2).toString()); + } + }; + return Pair.of(functionSignature, functionBuilder); + }; + } + /** * Wrapper the unary ExprValue function with default NULL and MISSING handling. */ @@ -183,4 +262,20 @@ public static SerializableBiFunction nullMissin } }; } + + /** + * Wrapper the triple ExprValue function with default NULL and MISSING handling. + */ + public SerializableTriFunction nullMissingHandling( + SerializableTriFunction function) { + return (v1, v2, v3) -> { + if (v1.isMissing() || v2.isMissing() || v3.isMissing()) { + return ExprValueUtils.missingValue(); + } else if (v1.isNull() || v2.isNull() || v3.isNull()) { + return ExprValueUtils.nullValue(); + } else { + return function.apply(v1, v2, v3); + } + }; + } } diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/SerializableNoArgFunction.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/SerializableNoArgFunction.java new file mode 100644 index 0000000000..eed0b3cc32 --- /dev/null +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/SerializableNoArgFunction.java @@ -0,0 +1,27 @@ +/* + * + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file 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.amazon.opendistroforelasticsearch.sql.expression.function; + +import java.io.Serializable; +import java.util.function.Supplier; + +/** + * Serializable no argument function. + */ +public interface SerializableNoArgFunction extends Supplier, Serializable { +} diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/SerializableTriFunction.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/SerializableTriFunction.java new file mode 100644 index 0000000000..c7c101f1bb --- /dev/null +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/SerializableTriFunction.java @@ -0,0 +1,40 @@ +/* + * + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file 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.amazon.opendistroforelasticsearch.sql.expression.function; + +import java.io.Serializable; + +/** + * Serializable Triple Function. + * + * @param the type of the first argument to the function + * @param the type of the second argument to the function + * @param the type of the third argument to the function + * @param the type of the result of the function + */ +public interface SerializableTriFunction extends Serializable { + /** + * Applies this function to the given arguments. + * + * @param t the first function argument + * @param u the second function argument + * @param v the third function argument + * @return the function result + */ + R apply(T t, U u, V v); +} diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/OperatorUtils.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/OperatorUtils.java deleted file mode 100644 index 39d4db9c09..0000000000 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/OperatorUtils.java +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file 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.amazon.opendistroforelasticsearch.sql.expression.operator; - -import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValue; -import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils; -import com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType; -import com.amazon.opendistroforelasticsearch.sql.data.type.ExprType; -import com.amazon.opendistroforelasticsearch.sql.expression.Expression; -import com.amazon.opendistroforelasticsearch.sql.expression.FunctionExpression; -import com.amazon.opendistroforelasticsearch.sql.expression.env.Environment; -import com.amazon.opendistroforelasticsearch.sql.expression.function.FunctionBuilder; -import com.amazon.opendistroforelasticsearch.sql.expression.function.FunctionName; -import java.util.List; -import java.util.Map; -import java.util.function.BiFunction; -import java.util.function.BiPredicate; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import lombok.experimental.UtilityClass; - -@UtilityClass -public class OperatorUtils { - /** - * Construct {@link FunctionBuilder} which call function with three arguments produced by - * observers.In general, if any operand evaluates to a MISSING value, the enclosing operator - * will return MISSING; if none of operands evaluates to a MISSING value but there is an - * operand evaluates to a NULL value, the enclosing operator will return NULL. - * - * @param functionName function name - * @param function {@link BiFunction} - * @param observer1 extract the value of type T from the first argument - * @param observer2 extract the value of type U from the first argument - * @param observer3 extract the value of type V from the first argument - * @param returnType return type - * @param the type of the first argument to the function - * @param the type of the second argument to the function - * @param the type of the third argument to the function - * @param the type of the result of the function - * @return {@link FunctionBuilder} - */ - public static FunctionBuilder tripleArgFunc( - FunctionName functionName, - TriFunction function, - Function observer1, - Function observer2, - Function observer3, - ExprCoreType returnType) { - return arguments -> new FunctionExpression(functionName, arguments) { - @Override - public ExprValue valueOf(Environment valueEnv) { - ExprValue arg1 = arguments.get(0).valueOf(valueEnv); - ExprValue arg2 = arguments.get(1).valueOf(valueEnv); - ExprValue arg3 = arguments.get(2).valueOf(valueEnv); - if (arg1.isMissing() || arg2.isMissing() || arg3.isMissing()) { - return ExprValueUtils.missingValue(); - } else if (arg1.isNull() || arg2.isNull() || arg3.isNull()) { - return ExprValueUtils.nullValue(); - } else { - return ExprValueUtils.fromObjectValue( - function.apply(observer1.apply(arg1), observer2.apply(arg2), observer3.apply(arg3))); - } - } - - @Override - public ExprType type() { - return returnType; - } - - @Override - public String toString() { - return String.format("%s(%s, %s, %s)", functionName, arguments.get(0).toString(), arguments - .get(1).toString(), arguments.get(2).toString()); - } - }; - } - - /** - * Construct {@link FunctionBuilder} which call function with arguments produced by observer. - * - * @param functionName function name - * @param function {@link BiFunction} - * @param observer extract the value of type T from the first argument - * @param returnType return type - * @param the type of the first and second argument to the function - * @param the type of the result of the function - * @return {@link FunctionBuilder} - */ - public static FunctionBuilder binaryOperator( - FunctionName functionName, - BiFunction function, - Function observer, - ExprCoreType returnType) { - return binaryOperator(functionName, function, observer, observer, returnType); - } - - /** - * Construct {@link FunctionBuilder} which call function with arguments produced by observer1 and - * observer2 In general, if any operand evaluates to a MISSING value, the enclosing operator will - * return MISSING; if none of operands evaluates to a MISSING value but there is an operand - * evaluates to a NULL value, the enclosing operator will return NULL. - * - * @param functionName function name - * @param function {@link BiFunction} - * @param observer1 extract the value of type T from the first argument - * @param observer2 extract the value of type U from the second argument - * @param returnType return type - * @param the type of the first argument to the function - * @param the type of the second argument to the function - * @param the type of the result of the function - * @return {@link FunctionBuilder} - */ - public static FunctionBuilder binaryOperator( - FunctionName functionName, - BiFunction function, - Function observer1, - Function observer2, - ExprCoreType returnType) { - return arguments -> - new FunctionExpression(functionName, arguments) { - @Override - public ExprValue valueOf(Environment env) { - ExprValue arg1 = arguments.get(0).valueOf(env); - ExprValue arg2 = arguments.get(1).valueOf(env); - if (arg1.isMissing() || arg2.isMissing()) { - return ExprValueUtils.missingValue(); - } else if (arg1.isNull() || arg2.isNull()) { - return ExprValueUtils.nullValue(); - } else { - return ExprValueUtils.fromObjectValue( - function.apply(observer1.apply(arg1), observer2.apply(arg2))); - } - } - - @Override - public ExprType type() { - return returnType; - } - - @Override - public String toString() { - return String.format("%s %s %s", arguments.get(0).toString(), functionName, arguments - .get(1).toString()); - } - }; - } - - /** - * Construct {@link FunctionBuilder} which call function with arguments produced by observer1 and - * observer2 In general, if any operand evaluates to a MISSING value, the enclosing operator will - * return MISSING; if none of operands evaluates to a MISSING value but there is an operand - * evaluates to a NULL value, the enclosing operator will return NULL. - * - * @param functionName function name - * @param function {@link BiFunction} - * @param observer1 extract the value of type T from the first argument - * @param observer2 extract the value of type U from the second argument - * @param returnType return type - * @param the type of the first argument to the function - * @param the type of the second argument to the function - * @param the type of the result of the function - * @return {@link FunctionBuilder} - */ - public static FunctionBuilder doubleArgFunc( - FunctionName functionName, - BiFunction function, - Function observer1, - Function observer2, - ExprType returnType) { - return arguments -> - new FunctionExpression(functionName, arguments) { - @Override - public ExprValue valueOf(Environment env) { - ExprValue arg1 = arguments.get(0).valueOf(env); - ExprValue arg2 = arguments.get(1).valueOf(env); - if (arg1.isMissing() || arg2.isMissing()) { - return ExprValueUtils.missingValue(); - } else if (arg1.isNull() || arg2.isNull()) { - return ExprValueUtils.nullValue(); - } else { - return ExprValueUtils.fromObjectValue( - function.apply(observer1.apply(arg1), observer2.apply(arg2))); - } - } - - @Override - public ExprType type() { - return returnType; - } - - @Override - public String toString() { - return String.format("%s(%s, %s)", functionName, arguments.get(0).toString(), arguments - .get(1).toString()); - } - }; - } - - /** - * Construct {@link FunctionBuilder} which call function with arguments produced by observer In - * general, if any operand evaluates to a MISSING value, the enclosing operator will return - * MISSING; if none of operands evaluates to a MISSING value but there is an operand evaluates to - * a NULL value, the enclosing operator will return NULL. - * - * @param functionName function name - * @param function {@link Function} - * @param observer extract the value of type T from the first argument - * @param returnType return type - * @param the type of the first argument to the function - * @param the type of the result of the function - * @return {@link FunctionBuilder} - */ - public static FunctionBuilder unaryOperator( - FunctionName functionName, - Function function, - Function observer, - ExprCoreType returnType) { - return arguments -> - new FunctionExpression(functionName, arguments) { - @Override - public ExprValue valueOf(Environment env) { - ExprValue arg1 = arguments.get(0).valueOf(env); - if (arg1.isMissing()) { - return ExprValueUtils.missingValue(); - } else if (arg1.isNull()) { - return ExprValueUtils.nullValue(); - } else { - return ExprValueUtils.fromObjectValue(function.apply(observer.apply(arg1))); - } - } - - @Override - public ExprType type() { - return returnType; - } - - @Override - public String toString() { - return String.format("%s(%s)", functionName, - arguments.stream() - .map(Object::toString) - .collect(Collectors.joining(", "))); - } - }; - } - - /** - * Construct {@link FunctionBuilder} which call function with no argument. - * - * @param functionName function name - * @param function {@link Function} - * @param returnType return type - * @param the type of the result to the function - * @return {@link FunctionBuilder} - */ - public static FunctionBuilder noArgFunction(FunctionName functionName, - Supplier function, - ExprCoreType returnType) { - return arguments -> new FunctionExpression(functionName, arguments) { - @Override - public ExprValue valueOf(Environment valueEnv) { - return ExprValueUtils.fromObjectValue(function.get()); - } - - @Override - public ExprType type() { - return returnType; - } - - @Override - public String toString() { - return String.format("%s()", functionName); - } - }; - } - - public interface TriFunction { - R apply(T t, U u, V v); - } -} diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/ArithmeticFunction.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/ArithmeticFunction.java index cf296ef056..472be1814f 100644 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/ArithmeticFunction.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/ArithmeticFunction.java @@ -19,19 +19,18 @@ import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.FLOAT; import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.INTEGER; import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.LONG; -import static com.amazon.opendistroforelasticsearch.sql.expression.operator.OperatorUtils.binaryOperator; +import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.SHORT; -import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprDoubleValue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprFloatValue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprIntegerValue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprLongValue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprNullValue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprShortValue; import com.amazon.opendistroforelasticsearch.sql.expression.function.BuiltinFunctionName; import com.amazon.opendistroforelasticsearch.sql.expression.function.BuiltinFunctionRepository; -import com.amazon.opendistroforelasticsearch.sql.expression.function.FunctionBuilder; -import com.amazon.opendistroforelasticsearch.sql.expression.function.FunctionName; +import com.amazon.opendistroforelasticsearch.sql.expression.function.FunctionDSL; import com.amazon.opendistroforelasticsearch.sql.expression.function.FunctionResolver; -import com.amazon.opendistroforelasticsearch.sql.expression.function.FunctionSignature; -import com.google.common.collect.ImmutableMap; -import java.util.Arrays; -import java.util.Map; -import java.util.function.BiFunction; import lombok.experimental.UtilityClass; /** @@ -58,80 +57,143 @@ public static void register(BuiltinFunctionRepository repository) { } private static FunctionResolver add() { - return new FunctionResolver( - BuiltinFunctionName.ADD.getName(), - scalarFunction(BuiltinFunctionName.ADD.getName(), - Math::addExact, - Math::addExact, - Float::sum, - Double::sum) + return FunctionDSL.define(BuiltinFunctionName.ADD.getName(), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> new ExprShortValue(v1.shortValue() + v2.shortValue())), + SHORT, SHORT, SHORT), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> new ExprIntegerValue(Math.addExact(v1.integerValue(), + v2.integerValue()))), + INTEGER, INTEGER, INTEGER), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> new ExprLongValue(Math.addExact(v1.longValue(), v2.longValue()))), + LONG, LONG, LONG), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> new ExprFloatValue(v1.floatValue() + v2.floatValue())), + FLOAT, FLOAT, FLOAT), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> new ExprDoubleValue(v1.doubleValue() + v2.doubleValue())), + DOUBLE, DOUBLE, DOUBLE) ); } private static FunctionResolver subtract() { - return new FunctionResolver( - BuiltinFunctionName.SUBTRACT.getName(), - scalarFunction(BuiltinFunctionName.SUBTRACT.getName(), - Math::subtractExact, - Math::subtractExact, - (v1, v2) -> v1 - v2, - (v1, v2) -> v1 - v2) + return FunctionDSL.define(BuiltinFunctionName.SUBTRACT.getName(), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> new ExprShortValue(v1.shortValue() - v2.shortValue())), + SHORT, SHORT, SHORT), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> new ExprIntegerValue(Math.subtractExact(v1.integerValue(), + v2.integerValue()))), + INTEGER, INTEGER, INTEGER), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> new ExprLongValue(Math.subtractExact(v1.longValue(), v2.longValue()))), + LONG, LONG, LONG), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> new ExprFloatValue(v1.floatValue() - v2.floatValue())), + FLOAT, FLOAT, FLOAT), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> new ExprDoubleValue(v1.doubleValue() - v2.doubleValue())), + DOUBLE, DOUBLE, DOUBLE) ); } private static FunctionResolver multiply() { - return new FunctionResolver( - BuiltinFunctionName.MULTIPLY.getName(), - scalarFunction(BuiltinFunctionName.MULTIPLY.getName(), - Math::multiplyExact, - Math::multiplyExact, - (v1, v2) -> v1 * v2, - (v1, v2) -> v1 * v2) + return FunctionDSL.define(BuiltinFunctionName.MULTIPLY.getName(), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> new ExprShortValue(v1.shortValue() * v2.shortValue())), + SHORT, SHORT, SHORT), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> new ExprIntegerValue(Math.multiplyExact(v1.integerValue(), + v2.integerValue()))), + INTEGER, INTEGER, INTEGER), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> new ExprLongValue(Math.multiplyExact(v1.longValue(), v2.longValue()))), + LONG, LONG, LONG), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> new ExprFloatValue(v1.floatValue() * v2.floatValue())), + FLOAT, FLOAT, FLOAT), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> new ExprDoubleValue(v1.doubleValue() * v2.doubleValue())), + DOUBLE, DOUBLE, DOUBLE) ); } private static FunctionResolver divide() { - return new FunctionResolver( - BuiltinFunctionName.DIVIDE.getName(), - scalarFunction(BuiltinFunctionName.DIVIDE.getName(), - (v1, v2) -> v2 == 0 ? null : v1 / v2, - (v1, v2) -> v2 == 0 ? null : v1 / v2, - (v1, v2) -> v2 == 0 ? null : v1 / v2, - (v1, v2) -> v2 == 0 ? null : v1 / v2) + return FunctionDSL.define(BuiltinFunctionName.DIVIDE.getName(), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> v2.shortValue() == 0 ? ExprNullValue.of() : + new ExprShortValue(v1.shortValue() / v2.shortValue())), + SHORT, SHORT, SHORT), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> v2.integerValue() == 0 ? ExprNullValue.of() : + new ExprIntegerValue(Math.floorDiv(v1.integerValue(), + v2.integerValue()))), + INTEGER, INTEGER, INTEGER), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> v2.longValue() == 0 ? ExprNullValue.of() : + new ExprLongValue(Math.floorDiv(v1.longValue(), v2.longValue()))), + LONG, LONG, LONG), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> v2.floatValue() == 0 ? ExprNullValue.of() : + new ExprFloatValue(v1.floatValue() / v2.floatValue())), + FLOAT, FLOAT, FLOAT), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> v2.doubleValue() == 0 ? ExprNullValue.of() : + new ExprDoubleValue(v1.doubleValue() / v2.doubleValue())), + DOUBLE, DOUBLE, DOUBLE) ); } private static FunctionResolver modules() { - return new FunctionResolver( - BuiltinFunctionName.MODULES.getName(), - scalarFunction(BuiltinFunctionName.MODULES.getName(), - (v1, v2) -> v2 == 0 ? null : v1 % v2, - (v1, v2) -> v2 == 0 ? null : v1 % v2, - (v1, v2) -> v2 == 0 ? null : v1 % v2, - (v1, v2) -> v2 == 0 ? null : v1 % v2) + return FunctionDSL.define(BuiltinFunctionName.MODULES.getName(), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> v2.shortValue() == 0 ? ExprNullValue.of() : + new ExprShortValue(v1.shortValue() % v2.shortValue())), + SHORT, SHORT, SHORT), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> v2.integerValue() == 0 ? ExprNullValue.of() : + new ExprIntegerValue(Math.floorMod(v1.integerValue(), + v2.integerValue()))), + INTEGER, INTEGER, INTEGER), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> v2.longValue() == 0 ? ExprNullValue.of() : + new ExprLongValue(Math.floorMod(v1.longValue(), v2.longValue()))), + LONG, LONG, LONG), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> v2.floatValue() == 0 ? ExprNullValue.of() : + new ExprFloatValue(v1.floatValue() % v2.floatValue())), + FLOAT, FLOAT, FLOAT), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> v2.doubleValue() == 0 ? ExprNullValue.of() : + new ExprDoubleValue(v1.doubleValue() % v2.doubleValue())), + DOUBLE, DOUBLE, DOUBLE) ); } - - private static Map scalarFunction( - FunctionName functionName, - BiFunction integerFunc, - BiFunction longFunc, - BiFunction floatFunc, - BiFunction doubleFunc) { - ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); - builder - .put(new FunctionSignature(functionName, Arrays.asList(INTEGER, INTEGER)), - binaryOperator(functionName, integerFunc, ExprValueUtils::getIntegerValue, - INTEGER)); - builder.put(new FunctionSignature(functionName, Arrays.asList(LONG, LONG)), - binaryOperator(functionName, longFunc, ExprValueUtils::getLongValue, LONG)); - builder.put(new FunctionSignature(functionName, Arrays.asList(FLOAT, FLOAT)), - binaryOperator(functionName, floatFunc, ExprValueUtils::getFloatValue, FLOAT)); - builder - .put(new FunctionSignature(functionName, Arrays.asList(DOUBLE, DOUBLE)), - binaryOperator(functionName, doubleFunc, ExprValueUtils::getDoubleValue, - DOUBLE)); - return builder.build(); - } } diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/MathematicalFunction.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/MathematicalFunction.java index 0ba0b6dbcb..2d8b854c9e 100644 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/MathematicalFunction.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/MathematicalFunction.java @@ -15,30 +15,40 @@ package com.amazon.opendistroforelasticsearch.sql.expression.operator.arthmetic; -import static com.amazon.opendistroforelasticsearch.sql.expression.operator.OperatorUtils.doubleArgFunc; -import static com.amazon.opendistroforelasticsearch.sql.expression.operator.OperatorUtils.noArgFunction; -import static com.amazon.opendistroforelasticsearch.sql.expression.operator.OperatorUtils.tripleArgFunc; -import static com.amazon.opendistroforelasticsearch.sql.expression.operator.OperatorUtils.unaryOperator; - -import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils; +import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.DOUBLE; +import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.FLOAT; +import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.INTEGER; +import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.LONG; +import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.SHORT; +import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.STRING; + +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprDoubleValue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprFloatValue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprIntegerValue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprLongValue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprNullValue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprShortValue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprStringValue; import com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType; +import com.amazon.opendistroforelasticsearch.sql.data.type.ExprType; import com.amazon.opendistroforelasticsearch.sql.expression.function.BuiltinFunctionName; import com.amazon.opendistroforelasticsearch.sql.expression.function.BuiltinFunctionRepository; import com.amazon.opendistroforelasticsearch.sql.expression.function.FunctionBuilder; +import com.amazon.opendistroforelasticsearch.sql.expression.function.FunctionDSL; import com.amazon.opendistroforelasticsearch.sql.expression.function.FunctionName; import com.amazon.opendistroforelasticsearch.sql.expression.function.FunctionResolver; import com.amazon.opendistroforelasticsearch.sql.expression.function.FunctionSignature; -import com.google.common.collect.ImmutableMap; +import com.amazon.opendistroforelasticsearch.sql.expression.function.SerializableFunction; +import com.google.common.collect.ImmutableList; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.Arrays; -import java.util.Collections; -import java.util.Map; +import java.util.List; import java.util.Random; -import java.util.function.BiFunction; -import java.util.function.Function; +import java.util.stream.Collectors; import java.util.zip.CRC32; import lombok.experimental.UtilityClass; +import org.apache.commons.lang3.tuple.Pair; @UtilityClass public class MathematicalFunction { @@ -86,10 +96,23 @@ public static void register(BuiltinFunctionRepository repository) { * LONG FLOAT -> FLOAT DOUBLE -> DOUBLE */ private static FunctionResolver abs() { - return new FunctionResolver( - BuiltinFunctionName.ABS.getName(), - singleArgumentFunction( - BuiltinFunctionName.ABS.getName(), Math::abs, Math::abs, Math::abs, Math::abs)); + return FunctionDSL.define(BuiltinFunctionName.ABS.getName(), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling(v -> new ExprShortValue(Math.abs(v.shortValue()))), + SHORT, SHORT), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling(v -> new ExprIntegerValue(Math.abs(v.integerValue()))), + INTEGER, INTEGER), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling(v -> new ExprLongValue(Math.abs(v.longValue()))), + LONG, LONG), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling(v -> new ExprFloatValue(Math.abs(v.floatValue()))), + FLOAT, FLOAT), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling(v -> new ExprDoubleValue(Math.abs(v.doubleValue()))), + DOUBLE, DOUBLE) + ); } /** @@ -97,33 +120,19 @@ private static FunctionResolver abs() { * to The supported signature of ceil/ceiling function is DOUBLE -> INTEGER */ private static FunctionResolver ceil() { - FunctionName functionName = BuiltinFunctionName.CEIL.getName(); - return new FunctionResolver( - functionName, - new ImmutableMap.Builder() - .put( - new FunctionSignature(functionName, Arrays.asList(ExprCoreType.DOUBLE)), - unaryOperator( - functionName, - v -> ((int) Math.ceil(v)), - ExprValueUtils::getDoubleValue, - ExprCoreType.INTEGER)) - .build()); + return FunctionDSL.define(BuiltinFunctionName.CEIL.getName(), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling(v -> new ExprIntegerValue(Math.ceil(v.doubleValue()))), + INTEGER, DOUBLE) + ); } private static FunctionResolver ceiling() { - FunctionName functionName = BuiltinFunctionName.CEILING.getName(); - return new FunctionResolver( - functionName, - new ImmutableMap.Builder() - .put( - new FunctionSignature(functionName, Arrays.asList(ExprCoreType.DOUBLE)), - unaryOperator( - functionName, - v -> ((int) Math.ceil(v)), - ExprValueUtils::getDoubleValue, - ExprCoreType.INTEGER)) - .build()); + return FunctionDSL.define(BuiltinFunctionName.CEILING.getName(), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling(v -> new ExprIntegerValue(Math.ceil(v.doubleValue()))), + INTEGER, DOUBLE) + ); } /** @@ -131,30 +140,23 @@ private static FunctionResolver ceiling() { * Convert number x from base a to base b * The supported signature of floor function is * (STRING, INTEGER, INTEGER) -> STRING + * (INTEGER, INTEGER, INTEGER) -> STRING */ private static FunctionResolver conv() { - FunctionName functionName = BuiltinFunctionName.CONV.getName(); - return new FunctionResolver( - functionName, - new ImmutableMap.Builder() - .put( - new FunctionSignature(functionName, - Arrays.asList(ExprCoreType.STRING, ExprCoreType.INTEGER, ExprCoreType.INTEGER)), - tripleArgFunc(functionName, - (num, fromBase, toBase) -> Integer.toString( - Integer.parseInt(num, fromBase), toBase), - ExprValueUtils::getStringValue, ExprValueUtils::getIntegerValue, - ExprValueUtils::getIntegerValue, ExprCoreType.STRING)) - .put( - new FunctionSignature(functionName, - Arrays.asList( - ExprCoreType.INTEGER, ExprCoreType.INTEGER, ExprCoreType.INTEGER)), - tripleArgFunc(functionName, - (num, fromBase, toBase) -> Integer.toString( - Integer.parseInt(num.toString(), fromBase), toBase), - ExprValueUtils::getIntegerValue, ExprValueUtils::getIntegerValue, - ExprValueUtils::getIntegerValue, ExprCoreType.STRING)) - .build()); + return FunctionDSL.define(BuiltinFunctionName.CONV.getName(), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling((x, a, b) -> new ExprStringValue( + Integer.toString(Integer.parseInt(x.stringValue(), a.integerValue()), + b.integerValue()) + )), + STRING, STRING, INTEGER, INTEGER), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling((x, a, b) -> new ExprStringValue( + Integer.toString(Integer.parseInt(x.integerValue().toString(), a.integerValue()), + b.integerValue()) + )), + STRING, INTEGER, INTEGER, INTEGER) + ); } /** @@ -164,20 +166,15 @@ private static FunctionResolver conv() { * STRING -> LONG */ private static FunctionResolver crc32() { - FunctionName functionName = BuiltinFunctionName.CRC32.getName(); - return new FunctionResolver(functionName, - new ImmutableMap.Builder() - .put( - new FunctionSignature(functionName, Arrays.asList(ExprCoreType.STRING)), - unaryOperator( - functionName, - v -> { - CRC32 crc = new CRC32(); - crc.update(v.getBytes()); - return crc.getValue(); - }, - ExprValueUtils::getStringValue, ExprCoreType.LONG)) - .build()); + return FunctionDSL.define(BuiltinFunctionName.CRC32.getName(), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling(v -> { + CRC32 crc = new CRC32(); + crc.update(v.stringValue().getBytes()); + return new ExprLongValue(crc.getValue()); + }), + LONG, STRING) + ); } /** @@ -186,12 +183,9 @@ private static FunctionResolver crc32() { * () -> DOUBLE */ private static FunctionResolver euler() { - FunctionName functionName = BuiltinFunctionName.E.getName(); - return new FunctionResolver(functionName, - new ImmutableMap.Builder() - .put(new FunctionSignature(functionName, Collections.emptyList()), - noArgFunction(functionName, () -> Math.E, ExprCoreType.DOUBLE)) - .build()); + return FunctionDSL.define(BuiltinFunctionName.E.getName(), + FunctionDSL.impl(() -> new ExprDoubleValue(Math.E), DOUBLE) + ); } /** @@ -199,9 +193,11 @@ private static FunctionResolver euler() { * of exp function is INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE */ private static FunctionResolver exp() { - return new FunctionResolver( - BuiltinFunctionName.EXP.getName(), - singleArgumentFunction(BuiltinFunctionName.EXP.getName(), Math::exp)); + return FunctionDSL.define(BuiltinFunctionName.EXP.getName(), + ExprCoreType.numberTypes().stream() + .map(type -> FunctionDSL.impl(FunctionDSL.nullMissingHandling( + v -> new ExprDoubleValue(Math.exp(v.doubleValue()))), + type, DOUBLE)).collect(Collectors.toList())); } /** @@ -209,18 +205,11 @@ private static FunctionResolver exp() { * The supported signature of floor function is DOUBLE -> INTEGER */ private static FunctionResolver floor() { - FunctionName functionName = BuiltinFunctionName.FLOOR.getName(); - return new FunctionResolver( - functionName, - new ImmutableMap.Builder() - .put( - new FunctionSignature(functionName, Arrays.asList(ExprCoreType.DOUBLE)), - unaryOperator( - functionName, - v -> ((int) Math.floor(v)), - ExprValueUtils::getDoubleValue, - ExprCoreType.INTEGER)) - .build()); + return FunctionDSL.define(BuiltinFunctionName.FLOOR.getName(), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling(v -> new ExprIntegerValue(Math.floor(v.doubleValue()))), + INTEGER, DOUBLE) + ); } /** @@ -228,9 +217,11 @@ private static FunctionResolver floor() { * ln function is INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE */ private static FunctionResolver ln() { - return new FunctionResolver( - BuiltinFunctionName.LN.getName(), - singleArgumentFunction(BuiltinFunctionName.LN.getName(), Math::log)); + return FunctionDSL.define(BuiltinFunctionName.LN.getName(), + ExprCoreType.numberTypes().stream() + .map(type -> FunctionDSL.impl(FunctionDSL.nullMissingHandling( + v -> new ExprDoubleValue(Math.log(v.doubleValue()))), + type, DOUBLE)).collect(Collectors.toList())); } /** @@ -239,44 +230,51 @@ private static FunctionResolver ln() { * INTEGER/LONG/FLOAT/DOUBLE]) -> DOUBLE */ private static FunctionResolver log() { - FunctionName functionName = BuiltinFunctionName.LOG.getName(); - return new FunctionResolver( - functionName, - new ImmutableMap.Builder() - .put( - new FunctionSignature(functionName, Arrays.asList(ExprCoreType.DOUBLE)), - unaryOperator( - functionName, Math::log, ExprValueUtils::getDoubleValue, ExprCoreType.DOUBLE)) - .put( - new FunctionSignature( - functionName, Arrays.asList(ExprCoreType.DOUBLE, ExprCoreType.DOUBLE)), - doubleArgFunc( - functionName, - (b, v) -> Math.log(v) / Math.log(b), - ExprValueUtils::getDoubleValue, - ExprValueUtils::getDoubleValue, - ExprCoreType.DOUBLE)) - .build()); + ImmutableList.Builder>> builder = new ImmutableList.Builder<>(); + + // build unary log(x), SHORT/INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE + for (ExprType type : ExprCoreType.numberTypes()) { + builder.add(FunctionDSL.impl(FunctionDSL + .nullMissingHandling(v -> new ExprDoubleValue(Math.log(v.doubleValue()))), + DOUBLE, type)); + } + + // build binary function log(b, x) + for (ExprType baseType : ExprCoreType.numberTypes()) { + for (ExprType numberType : ExprCoreType.numberTypes()) { + builder.add(FunctionDSL.impl(FunctionDSL + .nullMissingHandling((b, x) -> new ExprDoubleValue( + Math.log(x.doubleValue()) / Math.log(b.doubleValue()))), + DOUBLE, baseType, numberType)); + } + } + return FunctionDSL.define(BuiltinFunctionName.LOG.getName(), builder.build()); } + /** * Definition of log10(x) function. Calculate base-10 logarithm of x The supported signature of - * log function is INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE + * log function is SHORT/INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE */ private static FunctionResolver log10() { - return new FunctionResolver( - BuiltinFunctionName.LOG10.getName(), - singleArgumentFunction(BuiltinFunctionName.LOG10.getName(), Math::log10)); + return FunctionDSL.define(BuiltinFunctionName.LOG10.getName(), + ExprCoreType.numberTypes().stream() + .map(type -> FunctionDSL.impl(FunctionDSL.nullMissingHandling( + v -> new ExprDoubleValue(Math.log10(v.doubleValue()))), + type, DOUBLE)).collect(Collectors.toList())); } /** * Definition of log2(x) function. Calculate base-2 logarithm of x The supported signature of log - * function is INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE + * function is SHORT/INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE */ private static FunctionResolver log2() { - return new FunctionResolver( - BuiltinFunctionName.LOG2.getName(), - singleArgumentFunction(BuiltinFunctionName.LOG2.getName(), v -> Math.log(v) / Math.log(2))); + return FunctionDSL.define(BuiltinFunctionName.LOG2.getName(), + ExprCoreType.numberTypes().stream() + .map(type -> FunctionDSL.impl(FunctionDSL.nullMissingHandling( + v -> new ExprDoubleValue(Math.log(v.doubleValue()) / Math.log(2))), DOUBLE, type)) + .collect(Collectors.toList())); } /** @@ -287,13 +285,34 @@ private static FunctionResolver log2() { * -> wider type between types of x and y */ private static FunctionResolver mod() { - return new FunctionResolver( - BuiltinFunctionName.MOD.getName(), - doubleArgumentsFunction(BuiltinFunctionName.MOD.getName(), - (v1, v2) -> v2 == 0 ? null : v1 % v2, - (v1, v2) -> v2 == 0 ? null : v1 % v2, - (v1, v2) -> v2 == 0 ? null : v1 % v2, - (v1, v2) -> v2 == 0 ? null : v1 % v2)); + return FunctionDSL.define(BuiltinFunctionName.MOD.getName(), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> v2.shortValue() == 0 ? ExprNullValue.of() : + new ExprShortValue(v1.shortValue() % v2.shortValue())), + SHORT, SHORT, SHORT), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> v2.shortValue() == 0 ? ExprNullValue.of() : + new ExprIntegerValue(Math.floorMod(v1.integerValue(), + v2.integerValue()))), + INTEGER, INTEGER, INTEGER), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> v2.shortValue() == 0 ? ExprNullValue.of() : + new ExprLongValue(Math.floorMod(v1.longValue(), v2.longValue()))), + LONG, LONG, LONG), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> v2.shortValue() == 0 ? ExprNullValue.of() : + new ExprFloatValue(v1.floatValue() % v2.floatValue())), + FLOAT, FLOAT, FLOAT), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> v2.shortValue() == 0 ? ExprNullValue.of() : + new ExprDoubleValue(v1.doubleValue() % v2.doubleValue())), + DOUBLE, DOUBLE, DOUBLE) + ); } /** @@ -302,31 +321,51 @@ private static FunctionResolver mod() { * () -> DOUBLE */ private static FunctionResolver pi() { - FunctionName functionName = BuiltinFunctionName.PI.getName(); - return new FunctionResolver(functionName, - new ImmutableMap.Builder() - .put(new FunctionSignature(functionName, Collections.emptyList()), - noArgFunction(functionName, () -> Math.PI, ExprCoreType.DOUBLE)) - .build()); + return FunctionDSL.define(BuiltinFunctionName.PI.getName(), + FunctionDSL.impl(() -> new ExprDoubleValue(Math.PI), DOUBLE) + ); } /** * Definition of pow(x, y)/power(x, y) function. * Calculate the value of x raised to the power of y * The supported signature of pow/power function is - * (INTEGER, INTEGER) -> INTEGER - * (LONG, LONG) -> LONG - * (FLOAT, FLOAT) -> FLOAT + * (INTEGER, INTEGER) -> DOUBLE + * (LONG, LONG) -> DOUBLE + * (FLOAT, FLOAT) -> DOUBLE * (DOUBLE, DOUBLE) -> DOUBLE */ private static FunctionResolver pow() { - FunctionName functionName = BuiltinFunctionName.POW.getName(); - return new FunctionResolver(functionName, doubleArgumentsFunction(functionName, Math::pow)); + return FunctionDSL.define(BuiltinFunctionName.POW.getName(), powerFunctionImpl()); } private static FunctionResolver power() { - FunctionName functionName = BuiltinFunctionName.POWER.getName(); - return new FunctionResolver(functionName, doubleArgumentsFunction(functionName, Math::pow)); + return FunctionDSL.define(BuiltinFunctionName.POWER.getName(), powerFunctionImpl()); + } + + private List>> powerFunctionImpl() { + return Arrays.asList(FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> new ExprDoubleValue(Math.pow(v1.shortValue(), v2.shortValue()))), + DOUBLE, SHORT, SHORT), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> new ExprDoubleValue(Math.pow(v1.integerValue(), + v2.integerValue()))), + DOUBLE, INTEGER, INTEGER), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> new ExprDoubleValue(Math.pow(v1.longValue(), v2.longValue()))), + DOUBLE, LONG, LONG), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> new ExprDoubleValue(Math.pow(v1.floatValue(), v2.floatValue()))), + DOUBLE, FLOAT, FLOAT), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (v1, v2) -> new ExprDoubleValue(Math.pow(v1.doubleValue(), v2.doubleValue()))), + DOUBLE, DOUBLE, DOUBLE)); } /** @@ -339,18 +378,12 @@ private static FunctionResolver power() { * ([INTEGER]) -> FLOAT */ private static FunctionResolver rand() { - FunctionName functionName = BuiltinFunctionName.RAND.getName(); - return new FunctionResolver(functionName, - new ImmutableMap.Builder() - .put( - new FunctionSignature(functionName, Collections.emptyList()), - noArgFunction(functionName, () -> new Random().nextFloat(), ExprCoreType.FLOAT)) - .put( - new FunctionSignature(functionName, Arrays.asList(ExprCoreType.INTEGER)), - unaryOperator( - functionName, n -> new Random(n).nextFloat(), ExprValueUtils::getIntegerValue, - ExprCoreType.FLOAT)) - .build()); + return FunctionDSL.define(BuiltinFunctionName.RAND.getName(), + FunctionDSL.impl(() -> new ExprFloatValue(new Random().nextFloat()), FLOAT), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + v -> new ExprFloatValue(new Random(v.integerValue()).nextFloat())), FLOAT, INTEGER) + ); } /** @@ -363,58 +396,47 @@ private static FunctionResolver rand() { * (x: DOUBLE [, y: INTEGER]) -> DOUBLE */ private static FunctionResolver round() { - FunctionName functionName = BuiltinFunctionName.ROUND.getName(); - return new FunctionResolver(functionName, - new ImmutableMap.Builder() - .put( - new FunctionSignature(functionName, Arrays.asList(ExprCoreType.INTEGER)), - unaryOperator( - functionName, v -> (long) Math.round(v), ExprValueUtils::getIntegerValue, - ExprCoreType.LONG)) - .put( - new FunctionSignature(functionName, Arrays.asList(ExprCoreType.LONG)), - unaryOperator( - functionName, v -> (long) Math.round(v), ExprValueUtils::getLongValue, - ExprCoreType.LONG)) - .put( - new FunctionSignature(functionName, Arrays.asList(ExprCoreType.FLOAT)), - unaryOperator( - functionName, v -> (double) Math.round(v), ExprValueUtils::getFloatValue, - ExprCoreType.DOUBLE)) - .put( - new FunctionSignature(functionName, Arrays.asList(ExprCoreType.DOUBLE)), - unaryOperator( - functionName, v -> (double) Math.round(v), ExprValueUtils::getDoubleValue, - ExprCoreType.DOUBLE)) - .put( - new FunctionSignature( - functionName, Arrays.asList(ExprCoreType.INTEGER, ExprCoreType.INTEGER)), - doubleArgFunc(functionName, - (v1, v2) -> new BigDecimal(v1).setScale(v2, RoundingMode.HALF_UP).longValue(), - ExprValueUtils::getIntegerValue, ExprValueUtils::getIntegerValue, - ExprCoreType.LONG)) - .put( - new FunctionSignature( - functionName, Arrays.asList(ExprCoreType.LONG, ExprCoreType.INTEGER)), - doubleArgFunc(functionName, - (v1, v2) -> new BigDecimal(v1).setScale(v2, RoundingMode.HALF_UP).longValue(), - ExprValueUtils::getLongValue, ExprValueUtils::getIntegerValue, - ExprCoreType.LONG)) - .put( - new FunctionSignature( - functionName, Arrays.asList(ExprCoreType.FLOAT, ExprCoreType.INTEGER)), - doubleArgFunc(functionName, - (v1, v2) -> new BigDecimal(v1).setScale(v2, RoundingMode.HALF_UP).doubleValue(), - ExprValueUtils::getFloatValue, ExprValueUtils::getIntegerValue, - ExprCoreType.DOUBLE)) - .put( - new FunctionSignature( - functionName, Arrays.asList(ExprCoreType.DOUBLE, ExprCoreType.INTEGER)), - doubleArgFunc(functionName, - (v1, v2) -> new BigDecimal(v1).setScale(v2, RoundingMode.HALF_UP).doubleValue(), - ExprValueUtils::getDoubleValue, ExprValueUtils::getIntegerValue, - ExprCoreType.DOUBLE)) - .build()); + return FunctionDSL.define(BuiltinFunctionName.ROUND.getName(), + // rand(x) + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + v -> new ExprLongValue((long) Math.round(v.integerValue()))), + LONG, INTEGER), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + v -> new ExprLongValue((long) Math.round(v.longValue()))), + LONG, LONG), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + v -> new ExprDoubleValue((double) Math.round(v.floatValue()))), + DOUBLE, FLOAT), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + v -> new ExprDoubleValue((double) Math.round(v.doubleValue()))), + DOUBLE, DOUBLE), + + // rand(x, d) + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (x, d) -> new ExprLongValue( + new BigDecimal(x.integerValue()).setScale(d.integerValue(), + RoundingMode.HALF_UP).longValue())), + LONG, INTEGER, INTEGER), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (x, d) -> new ExprLongValue(new BigDecimal(x.longValue()).setScale(d.integerValue(), + RoundingMode.HALF_UP).longValue())), + LONG, LONG, INTEGER), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (x, d) -> new ExprDoubleValue(new BigDecimal(x.floatValue()) + .setScale(d.integerValue(), RoundingMode.HALF_UP).doubleValue())), + DOUBLE, FLOAT, INTEGER), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (x, d) -> new ExprDoubleValue(new BigDecimal(x.doubleValue()) + .setScale(d.integerValue(), RoundingMode.HALF_UP).doubleValue())), + DOUBLE, DOUBLE, INTEGER)); } /** @@ -422,20 +444,14 @@ private static FunctionResolver round() { * Returns the sign of the argument as -1, 0, or 1 * depending on whether x is negative, zero, or positive * The supported signature is - * INTEGER/LONG/FLOAT/DOUBLE -> INTEGER + * SHORT/INTEGER/LONG/FLOAT/DOUBLE -> INTEGER */ private static FunctionResolver sign() { - FunctionName functionName = BuiltinFunctionName.SIGN.getName(); - return new FunctionResolver( - functionName, - new ImmutableMap.Builder() - .put( - new FunctionSignature( - functionName, Arrays.asList(ExprCoreType.DOUBLE)), - unaryOperator( - functionName, v -> (int) Math.signum(v), ExprValueUtils::getDoubleValue, - ExprCoreType.INTEGER)) - .build()); + return FunctionDSL.define(BuiltinFunctionName.SIGN.getName(), + ExprCoreType.numberTypes().stream() + .map(type -> FunctionDSL.impl(FunctionDSL.nullMissingHandling( + v -> new ExprIntegerValue(Math.signum(v.doubleValue()))), + INTEGER, type)).collect(Collectors.toList())); } /** @@ -445,56 +461,49 @@ private static FunctionResolver sign() { * INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE */ private static FunctionResolver sqrt() { - FunctionName functionName = BuiltinFunctionName.SQRT.getName(); - return new FunctionResolver( - functionName, - singleArgumentFunction( - functionName, - v -> v < 0 ? null : Math.sqrt(v))); + return FunctionDSL.define(BuiltinFunctionName.SQRT.getName(), + ExprCoreType.numberTypes().stream() + .map(type -> FunctionDSL.impl(FunctionDSL.nullMissingHandling( + v -> v.doubleValue() < 0 ? ExprNullValue.of() : + new ExprDoubleValue(Math.sqrt(v.doubleValue()))), + DOUBLE, type)).collect(Collectors.toList())); } /** * Definition of truncate(x, d) function. * Returns the number x, truncated to d decimal places * The supported signature of round function is - * (x: INTEGER, y: INTEGER) -> INTEGER + * (x: INTEGER, y: INTEGER) -> LONG * (x: LONG, y: INTEGER) -> LONG - * (x: FLOAT, y: INTEGER) -> FLOAT + * (x: FLOAT, y: INTEGER) -> DOUBLE * (x: DOUBLE, y: INTEGER) -> DOUBLE */ private static FunctionResolver truncate() { - FunctionName functionName = BuiltinFunctionName.TRUNCATE.getName(); - return new FunctionResolver(functionName, - new ImmutableMap.Builder() - .put( - new FunctionSignature( - functionName, Arrays.asList(ExprCoreType.INTEGER, ExprCoreType.INTEGER)), - doubleArgFunc(functionName, - (v1, v2) -> new BigDecimal(v1).setScale(v2, RoundingMode.DOWN).longValue(), - ExprValueUtils::getIntegerValue, ExprValueUtils::getIntegerValue, - ExprCoreType.LONG)) - .put( - new FunctionSignature( - functionName, Arrays.asList(ExprCoreType.LONG, ExprCoreType.INTEGER)), - doubleArgFunc(functionName, - (v1, v2) -> new BigDecimal(v1).setScale(v2, RoundingMode.DOWN).longValue(), - ExprValueUtils::getLongValue, ExprValueUtils::getIntegerValue, - ExprCoreType.LONG)) - .put( - new FunctionSignature( - functionName, Arrays.asList(ExprCoreType.FLOAT, ExprCoreType.INTEGER)), - doubleArgFunc(functionName, - (v1, v2) -> new BigDecimal(v1).setScale(v2, RoundingMode.DOWN).doubleValue(), - ExprValueUtils::getFloatValue, ExprValueUtils::getIntegerValue, - ExprCoreType.DOUBLE)) - .put( - new FunctionSignature( - functionName, Arrays.asList(ExprCoreType.DOUBLE, ExprCoreType.INTEGER)), - doubleArgFunc(functionName, - (v1, v2) -> new BigDecimal(v1).setScale(v2, RoundingMode.DOWN).doubleValue(), - ExprValueUtils::getDoubleValue, ExprValueUtils::getIntegerValue, - ExprCoreType.DOUBLE)) - .build()); + return FunctionDSL.define(BuiltinFunctionName.TRUNCATE.getName(), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (x, y) -> new ExprLongValue( + new BigDecimal(x.integerValue()).setScale(y.integerValue(), + RoundingMode.DOWN).longValue())), + LONG, INTEGER, INTEGER), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (x, y) -> new ExprLongValue( + new BigDecimal(x.integerValue()).setScale(y.integerValue(), + RoundingMode.DOWN).longValue())), + LONG, LONG, INTEGER), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (x, y) -> new ExprDoubleValue( + new BigDecimal(x.floatValue()).setScale(y.integerValue(), + RoundingMode.DOWN).doubleValue())), + DOUBLE, FLOAT, INTEGER), + FunctionDSL.impl( + FunctionDSL.nullMissingHandling( + (x, y) -> new ExprDoubleValue( + new BigDecimal(x.doubleValue()).setScale(y.integerValue(), + RoundingMode.DOWN).doubleValue())), + DOUBLE, DOUBLE, INTEGER)); } /** @@ -505,10 +514,12 @@ private static FunctionResolver truncate() { * INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE */ private static FunctionResolver acos() { - FunctionName functionName = BuiltinFunctionName.ACOS.getName(); - return new FunctionResolver( - functionName, - singleArgumentFunction(functionName, v -> v < -1 || v > 1 ? null : Math.acos(v))); + return FunctionDSL.define(BuiltinFunctionName.ACOS.getName(), + ExprCoreType.numberTypes().stream() + .map(type -> FunctionDSL.impl(FunctionDSL.nullMissingHandling( + v -> v.doubleValue() < -1 || v.doubleValue() > 1 ? ExprNullValue.of() : + new ExprDoubleValue(Math.acos(v.doubleValue()))), + DOUBLE, type)).collect(Collectors.toList())); } /** @@ -519,10 +530,12 @@ private static FunctionResolver acos() { * INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE */ private static FunctionResolver asin() { - FunctionName functionName = BuiltinFunctionName.ASIN.getName(); - return new FunctionResolver( - functionName, - singleArgumentFunction(functionName, v -> v < -1 || v > 1 ? null : Math.asin(v))); + return FunctionDSL.define(BuiltinFunctionName.ASIN.getName(), + ExprCoreType.numberTypes().stream() + .map(type -> FunctionDSL.impl(FunctionDSL.nullMissingHandling( + v -> v.doubleValue() < -1 || v.doubleValue() > 1 ? ExprNullValue.of() : + new ExprDoubleValue(Math.asin(v.doubleValue()))), + DOUBLE, type)).collect(Collectors.toList())); } /** @@ -534,20 +547,19 @@ private static FunctionResolver asin() { * (x: INTEGER/LONG/FLOAT/DOUBLE, y: INTEGER/LONG/FLOAT/DOUBLE) -> DOUBLE */ private static FunctionResolver atan() { - FunctionName functionName = BuiltinFunctionName.ATAN.getName(); - return new FunctionResolver(functionName, - new ImmutableMap.Builder() - .put( - new FunctionSignature(functionName, Arrays.asList(ExprCoreType.DOUBLE)), - unaryOperator( - functionName, Math::atan, ExprValueUtils::getDoubleValue, ExprCoreType.DOUBLE)) - .put( - new FunctionSignature( - functionName, Arrays.asList(ExprCoreType.DOUBLE, ExprCoreType.DOUBLE)), - doubleArgFunc(functionName, - Math::atan2, ExprValueUtils::getDoubleValue, ExprValueUtils::getDoubleValue, - ExprCoreType.DOUBLE)) - .build()); + ImmutableList.Builder>> builder = new ImmutableList.Builder<>(); + + for (ExprType type : ExprCoreType.numberTypes()) { + builder.add(FunctionDSL.impl(FunctionDSL + .nullMissingHandling(x -> new ExprDoubleValue(Math.atan(x.doubleValue()))), type, + DOUBLE)); + builder.add(FunctionDSL.impl(FunctionDSL + .nullMissingHandling((y, x) -> new ExprDoubleValue(Math.atan2(y.doubleValue(), + x.doubleValue()))), DOUBLE, type, type)); + } + + return FunctionDSL.define(BuiltinFunctionName.ATAN.getName(), builder.build()); } /** @@ -558,16 +570,16 @@ private static FunctionResolver atan() { * (x: INTEGER/LONG/FLOAT/DOUBLE, y: INTEGER/LONG/FLOAT/DOUBLE) -> DOUBLE */ private static FunctionResolver atan2() { - FunctionName functionName = BuiltinFunctionName.ATAN2.getName(); - return new FunctionResolver(functionName, - new ImmutableMap.Builder() - .put( - new FunctionSignature( - functionName, Arrays.asList(ExprCoreType.DOUBLE, ExprCoreType.DOUBLE)), - doubleArgFunc(functionName, - Math::atan2, ExprValueUtils::getDoubleValue, ExprValueUtils::getDoubleValue, - ExprCoreType.DOUBLE)) - .build()); + ImmutableList.Builder>> builder = new ImmutableList.Builder<>(); + + for (ExprType type : ExprCoreType.numberTypes()) { + builder.add(FunctionDSL.impl(FunctionDSL + .nullMissingHandling((y, x) -> new ExprDoubleValue(Math.atan2(y.doubleValue(), + x.doubleValue()))), DOUBLE, type, type)); + } + + return FunctionDSL.define(BuiltinFunctionName.ATAN2.getName(), builder.build()); } /** @@ -577,8 +589,11 @@ private static FunctionResolver atan2() { * INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE */ private static FunctionResolver cos() { - FunctionName functionName = BuiltinFunctionName.COS.getName(); - return new FunctionResolver(functionName, singleArgumentFunction(functionName, Math::cos)); + return FunctionDSL.define(BuiltinFunctionName.COS.getName(), + ExprCoreType.numberTypes().stream() + .map(type -> FunctionDSL.impl(FunctionDSL.nullMissingHandling( + v -> new ExprDoubleValue(Math.cos(v.doubleValue()))), + DOUBLE, type)).collect(Collectors.toList())); } /** @@ -588,15 +603,18 @@ private static FunctionResolver cos() { * INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE */ private static FunctionResolver cot() { - FunctionName functionName = BuiltinFunctionName.COT.getName(); - return new FunctionResolver( - functionName, - singleArgumentFunction(functionName, v -> { - if (v == 0) { - throw new ArithmeticException(String.format("Out of range value for cot(%s)", v)); - } - return 1 / Math.tan(v); - })); + return FunctionDSL.define(BuiltinFunctionName.COT.getName(), + ExprCoreType.numberTypes().stream() + .map(type -> FunctionDSL.impl(FunctionDSL.nullMissingHandling( + v -> { + Double value = v.doubleValue(); + if (value == 0) { + throw new ArithmeticException( + String.format("Out of range value for cot(%s)", value)); + } + return new ExprDoubleValue(1 / Math.tan(value)); + }), + DOUBLE, type)).collect(Collectors.toList())); } /** @@ -606,9 +624,11 @@ private static FunctionResolver cot() { * INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE */ private static FunctionResolver degrees() { - FunctionName functionName = BuiltinFunctionName.DEGREES.getName(); - return new FunctionResolver( - functionName, singleArgumentFunction(functionName, Math::toDegrees)); + return FunctionDSL.define(BuiltinFunctionName.DEGREES.getName(), + ExprCoreType.numberTypes().stream() + .map(type -> FunctionDSL.impl(FunctionDSL.nullMissingHandling( + v -> new ExprDoubleValue(Math.toDegrees(v.doubleValue()))), + type, DOUBLE)).collect(Collectors.toList())); } /** @@ -618,9 +638,11 @@ private static FunctionResolver degrees() { * INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE */ private static FunctionResolver radians() { - FunctionName functionName = BuiltinFunctionName.RADIANS.getName(); - return new FunctionResolver( - functionName, singleArgumentFunction(functionName, Math::toRadians)); + return FunctionDSL.define(BuiltinFunctionName.RADIANS.getName(), + ExprCoreType.numberTypes().stream() + .map(type -> FunctionDSL.impl(FunctionDSL.nullMissingHandling( + v -> new ExprDoubleValue(Math.toRadians(v.doubleValue()))), + DOUBLE, type)).collect(Collectors.toList())); } /** @@ -630,8 +652,11 @@ private static FunctionResolver radians() { * INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE */ private static FunctionResolver sin() { - FunctionName functionName = BuiltinFunctionName.SIN.getName(); - return new FunctionResolver(functionName, singleArgumentFunction(functionName, Math::sin)); + return FunctionDSL.define(BuiltinFunctionName.SIN.getName(), + ExprCoreType.numberTypes().stream() + .map(type -> FunctionDSL.impl(FunctionDSL.nullMissingHandling( + v -> new ExprDoubleValue(Math.sin(v.doubleValue()))), + DOUBLE, type)).collect(Collectors.toList())); } /** @@ -641,93 +666,10 @@ private static FunctionResolver sin() { * INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE */ private static FunctionResolver tan() { - FunctionName functionName = BuiltinFunctionName.TAN.getName(); - return new FunctionResolver(functionName, singleArgumentFunction(functionName, Math::tan)); - } - - /** - * Util method to generate single argument function bundles. Applicable for INTEGER -> INTEGER - * LONG -> LONG FLOAT -> FLOAT DOUBLE -> DOUBLE - */ - private static Map singleArgumentFunction( - FunctionName functionName, - Function integerFunc, - Function longFunc, - Function floatFunc, - Function doubleFunc) { - ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); - builder.put( - new FunctionSignature(functionName, Arrays.asList(ExprCoreType.INTEGER)), - unaryOperator( - functionName, integerFunc, ExprValueUtils::getIntegerValue, ExprCoreType.INTEGER)); - builder.put( - new FunctionSignature(functionName, Arrays.asList(ExprCoreType.LONG)), - unaryOperator(functionName, longFunc, ExprValueUtils::getLongValue, ExprCoreType.LONG)); - builder.put( - new FunctionSignature(functionName, Arrays.asList(ExprCoreType.FLOAT)), - unaryOperator(functionName, floatFunc, ExprValueUtils::getFloatValue, ExprCoreType.FLOAT)); - builder.put( - new FunctionSignature(functionName, Arrays.asList(ExprCoreType.DOUBLE)), - unaryOperator( - functionName, doubleFunc, ExprValueUtils::getDoubleValue, ExprCoreType.DOUBLE)); - return builder.build(); - } - - /** Util method to generate single argument function bundles. Applicable for DOUBLE -> DOUBLE */ - private static Map singleArgumentFunction( - FunctionName functionName, Function doubleFunc) { - ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); - return builder - .put( - new FunctionSignature(functionName, Arrays.asList(ExprCoreType.DOUBLE)), - unaryOperator( - functionName, doubleFunc, ExprValueUtils::getDoubleValue, ExprCoreType.DOUBLE)) - .build(); - } - - private static Map doubleArgumentsFunction( - FunctionName functionName, - BiFunction intFunc, - BiFunction longFunc, - BiFunction floatFunc, - BiFunction doubleFunc) { - return new ImmutableMap.Builder() - .put( - new FunctionSignature( - functionName, Arrays.asList(ExprCoreType.INTEGER, ExprCoreType.INTEGER)), - doubleArgFunc( - functionName, intFunc, ExprValueUtils::getIntegerValue, - ExprValueUtils::getIntegerValue, ExprCoreType.INTEGER)) - .put( - new FunctionSignature( - functionName, Arrays.asList(ExprCoreType.LONG, ExprCoreType.LONG)), - doubleArgFunc( - functionName, longFunc, ExprValueUtils::getLongValue, - ExprValueUtils::getLongValue, ExprCoreType.LONG)) - .put( - new FunctionSignature( - functionName, Arrays.asList(ExprCoreType.FLOAT, ExprCoreType.FLOAT)), - doubleArgFunc( - functionName, floatFunc, ExprValueUtils::getFloatValue, - ExprValueUtils::getFloatValue, ExprCoreType.FLOAT)) - .put( - new FunctionSignature( - functionName, Arrays.asList(ExprCoreType.DOUBLE, ExprCoreType.DOUBLE)), - doubleArgFunc( - functionName, doubleFunc, ExprValueUtils::getDoubleValue, - ExprValueUtils::getDoubleValue, ExprCoreType.DOUBLE)) - .build(); - } - - private static Map doubleArgumentsFunction( - FunctionName functionName, - BiFunction doubleFunc) { - return new ImmutableMap.Builder() - .put( - new FunctionSignature( - functionName, Arrays.asList(ExprCoreType.DOUBLE, ExprCoreType.DOUBLE)), - doubleArgFunc( - functionName, doubleFunc, ExprValueUtils::getDoubleValue, - ExprValueUtils::getDoubleValue, ExprCoreType.DOUBLE)).build(); + return FunctionDSL.define(BuiltinFunctionName.TAN.getName(), + ExprCoreType.numberTypes().stream() + .map(type -> FunctionDSL.impl(FunctionDSL.nullMissingHandling( + v -> new ExprDoubleValue(Math.tan(v.doubleValue()))), + DOUBLE, type)).collect(Collectors.toList())); } } diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprNumberValueTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprNumberValueTest.java new file mode 100644 index 0000000000..fe33301818 --- /dev/null +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprNumberValueTest.java @@ -0,0 +1,34 @@ +/* + * + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file 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.amazon.opendistroforelasticsearch.sql.data.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.amazon.opendistroforelasticsearch.sql.exception.ExpressionEvaluationException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class ExprNumberValueTest { + @Test + public void getShortValueFromIncompatibleExprValue() { + ExprBooleanValue booleanValue = ExprBooleanValue.of(true); + ExpressionEvaluationException exception = Assertions + .assertThrows(ExpressionEvaluationException.class, () -> booleanValue.shortValue()); + assertEquals("invalid to get shortValue from value of type BOOLEAN", exception.getMessage()); + } +} diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/aggregation/AvgAggregatorTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/aggregation/AvgAggregatorTest.java index cc54b5cef5..f90d061dde 100644 --- a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/aggregation/AvgAggregatorTest.java +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/aggregation/AvgAggregatorTest.java @@ -74,7 +74,7 @@ public void test_to_string() { public void test_nested_to_string() { Aggregator avgAggregator = dsl.avg(dsl.multiply(DSL.ref("integer_value", INTEGER), DSL.literal(ExprValueUtils.integerValue(10)))); - assertEquals(String.format("avg(%s * %d)", DSL.ref("integer_value", INTEGER), 10), + assertEquals(String.format("avg(*(%s, %d))", DSL.ref("integer_value", INTEGER), 10), avgAggregator.toString()); } } \ No newline at end of file diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/aggregation/SumAggregatorTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/aggregation/SumAggregatorTest.java index c35e1c7832..4697a7c7ff 100644 --- a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/aggregation/SumAggregatorTest.java +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/aggregation/SumAggregatorTest.java @@ -113,7 +113,7 @@ public void test_to_string() { public void test_nested_to_string() { Aggregator sumAggregator = dsl.sum(dsl.multiply(DSL.ref("integer_value", INTEGER), DSL.literal(ExprValueUtils.integerValue(10)))); - assertEquals(String.format("sum(%s * %d)", DSL.ref("integer_value", INTEGER), 10), + assertEquals(String.format("sum(*(%s, %d))", DSL.ref("integer_value", INTEGER), 10), sumAggregator.toString()); } } \ No newline at end of file diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/ArithmeticFunctionTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/ArithmeticFunctionTest.java index 877042ef32..cc55190635 100644 --- a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/ArithmeticFunctionTest.java +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/ArithmeticFunctionTest.java @@ -21,11 +21,17 @@ import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.LITERAL_NULL; import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.integerValue; import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.INTEGER; +import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.SHORT; import static com.amazon.opendistroforelasticsearch.sql.expression.DSL.literal; import static com.amazon.opendistroforelasticsearch.sql.expression.DSL.ref; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprDoubleValue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprFloatValue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprIntegerValue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprLongValue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprShortValue; import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValue; import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils; import com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType; @@ -39,7 +45,6 @@ import java.util.Arrays; import java.util.List; import java.util.function.Function; -import java.util.stream.Collectors; import java.util.stream.Stream; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; @@ -51,10 +56,11 @@ class ArithmeticFunctionTest extends ExpressionTestBase { private static Stream arithmeticFunctionArguments() { - List numberOp1 = Stream.of(3, 3L, 3f, 3D) - .map(ExprValueUtils::fromObjectValue).collect(Collectors.toList()); - List numberOp2 = Stream.of(2, 2L, 2f, 2D) - .map(ExprValueUtils::fromObjectValue).collect(Collectors.toList()); + List numberOp1 = Arrays.asList(new ExprShortValue(3), new ExprIntegerValue(3), + new ExprLongValue(3L), new ExprFloatValue(3f), new ExprDoubleValue(3D)); + List numberOp2 = + Arrays.asList(new ExprShortValue(2), new ExprIntegerValue(2), new ExprLongValue(3L), + new ExprFloatValue(2f), new ExprDoubleValue(2D)); return Lists.cartesianProduct(numberOp1, numberOp2).stream() .map(list -> Arguments.of(list.get(0), list.get(1))); } @@ -72,7 +78,7 @@ public void add(ExprValue op1, ExprValue op2) { ExprType expectedType = WideningTypeRule.max(op1.type(), op2.type()); assertEquals(expectedType, expression.type()); assertValueEqual(BuiltinFunctionName.ADD, expectedType, op1, op2, expression.valueOf(null)); - assertEquals(String.format("%s + %s", op1.toString(), op2.toString()), expression.toString()); + assertEquals(String.format("+(%s, %s)", op1.toString(), op2.toString()), expression.toString()); } @ParameterizedTest(name = "{0}(int,null)") @@ -148,7 +154,8 @@ public void subtract(ExprValue op1, ExprValue op2) { assertEquals(expectedType, expression.type()); assertValueEqual(BuiltinFunctionName.SUBTRACT, expectedType, op1, op2, expression.valueOf(null)); - assertEquals(String.format("%s - %s", op1.toString(), op2.toString()), expression.toString()); + assertEquals(String.format("-(%s, %s)", op1.toString(), op2.toString()), + expression.toString()); } @ParameterizedTest(name = "multiply({1}, {2})") @@ -159,7 +166,8 @@ public void multiply(ExprValue op1, ExprValue op2) { assertEquals(expectedType, expression.type()); assertValueEqual(BuiltinFunctionName.MULTIPLY, expectedType, op1, op2, expression.valueOf(null)); - assertEquals(String.format("%s * %s", op1.toString(), op2.toString()), expression.toString()); + assertEquals(String.format("*(%s, %s)", op1.toString(), op2.toString()), + expression.toString()); } @ParameterizedTest(name = "divide({1}, {2})") @@ -169,13 +177,14 @@ public void divide(ExprValue op1, ExprValue op2) { ExprType expectedType = WideningTypeRule.max(op1.type(), op2.type()); assertEquals(expectedType, expression.type()); assertValueEqual(BuiltinFunctionName.DIVIDE, expectedType, op1, op2, expression.valueOf(null)); - assertEquals(String.format("%s / %s", op1.toString(), op2.toString()), expression.toString()); + assertEquals(String.format("/(%s, %s)", op1.toString(), op2.toString()), + expression.toString()); - expression = dsl.divide(literal(op1), literal(0)); - expectedType = WideningTypeRule.max(op1.type(), INTEGER); + expression = dsl.divide(literal(op1), literal(new ExprShortValue(0))); + expectedType = WideningTypeRule.max(op1.type(), SHORT); assertEquals(expectedType, expression.type()); assertTrue(expression.valueOf(valueEnv()).isNull()); - assertEquals(String.format("%s / 0", op1.toString()), expression.toString()); + assertEquals(String.format("/(%s, 0)", op1.toString()), expression.toString()); } @ParameterizedTest(name = "module({1}, {2})") @@ -185,13 +194,14 @@ public void module(ExprValue op1, ExprValue op2) { ExprType expectedType = WideningTypeRule.max(op1.type(), op2.type()); assertEquals(expectedType, expression.type()); assertValueEqual(BuiltinFunctionName.MODULES, expectedType, op1, op2, expression.valueOf(null)); - assertEquals(op1.toString() + " % " + op2.toString(), expression.toString()); + assertEquals(String.format("%%(%s, %s)", op1.toString(), op2.toString()), + expression.toString()); - expression = dsl.module(literal(op1), literal(0)); - expectedType = WideningTypeRule.max(op1.type(), INTEGER); + expression = dsl.module(literal(op1), literal(new ExprShortValue(0))); + expectedType = WideningTypeRule.max(op1.type(), SHORT); assertEquals(expectedType, expression.type()); assertTrue(expression.valueOf(valueEnv()).isNull()); - assertEquals(op1.toString() + " % 0", expression.toString()); + assertEquals(String.format("%%(%s, 0)", op1.toString()), expression.toString()); } protected void assertValueEqual(BuiltinFunctionName builtinFunctionName, ExprType type, @@ -199,6 +209,29 @@ protected void assertValueEqual(BuiltinFunctionName builtinFunctionName, ExprTyp ExprValue op2, ExprValue actual) { switch ((ExprCoreType) type) { + case SHORT: + Short vs1 = op1.shortValue(); + Short vs2 = op2.shortValue(); + Integer vsActual = actual.integerValue(); + switch (builtinFunctionName) { + case ADD: + assertEquals(vs1 + vs2, vsActual); + return; + case SUBTRACT: + assertEquals(vs1 - vs2, vsActual); + return; + case DIVIDE: + assertEquals(vs1 / vs2, vsActual); + return; + case MULTIPLY: + assertEquals(vs1 * vs2, vsActual); + return; + case MODULES: + assertEquals(vs1 % vs2, vsActual); + return; + default: + throw new IllegalStateException("illegal function name " + builtinFunctionName); + } case INTEGER: Integer vi1 = ExprValueUtils.getIntegerValue(op1); Integer vi2 = ExprValueUtils.getIntegerValue(op2); diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/MathematicalFunctionTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/MathematicalFunctionTest.java index bf1cf97ead..2fd8a99e8d 100644 --- a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/MathematicalFunctionTest.java +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/MathematicalFunctionTest.java @@ -27,6 +27,7 @@ import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.FLOAT; import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.INTEGER; import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.LONG; +import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.SHORT; import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.STRING; import static com.amazon.opendistroforelasticsearch.sql.utils.MatcherUtils.hasType; import static com.amazon.opendistroforelasticsearch.sql.utils.MatcherUtils.hasValue; @@ -37,6 +38,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprShortValue; import com.amazon.opendistroforelasticsearch.sql.expression.DSL; import com.amazon.opendistroforelasticsearch.sql.expression.ExpressionTestBase; import com.amazon.opendistroforelasticsearch.sql.expression.FunctionExpression; @@ -141,6 +143,19 @@ public void abs_double_value(Double value) { assertEquals(String.format("abs(%s)", value.toString()), abs.toString()); } + /** + * Test abs with short value. + */ + @ParameterizedTest(name = "abs({0})") + @ValueSource(shorts = {-2, 2}) + public void abs_short_value(Short value) { + FunctionExpression abs = dsl.abs(DSL.literal(new ExprShortValue(value))); + assertThat( + abs.valueOf(valueEnv()), + allOf(hasType(SHORT), hasValue(Integer.valueOf(Math.abs(value)).shortValue()))); + assertEquals(String.format("abs(%s)", value.toString()), abs.toString()); + } + @Test public void abs_null_value() { assertTrue(dsl.abs(DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER)).valueOf(valueEnv()).isNull()); @@ -1024,6 +1039,25 @@ public void log2_missing_value() { assertTrue(log.valueOf(valueEnv()).isMissing()); } + /** + * Test mod with short value. + */ + @ParameterizedTest(name = "mod({0}, {1})") + @MethodSource("testLogIntegerArguments") + public void mod_short_value(Integer v1, Integer v2) { + FunctionExpression mod = dsl.mod(DSL.literal(v1.shortValue()), DSL.literal(v2.shortValue())); + + assertThat( + mod.valueOf(valueEnv()), + allOf(hasType(SHORT), + hasValue(Integer.valueOf(v1.shortValue() % v2.shortValue()).shortValue()))); + assertEquals(String.format("mod(%s, %s)", v1, v2), mod.toString()); + + mod = dsl.mod(DSL.literal(v1.shortValue()), DSL.literal(new ExprShortValue(0))); + assertEquals(SHORT, mod.type()); + assertTrue(mod.valueOf(valueEnv()).isNull()); + } + /** * Test mod with integer value. */ @@ -1148,6 +1182,26 @@ public void mod_null_missing() { assertTrue(mod.valueOf(valueEnv()).isMissing()); } + /** + * Test pow/power with integer value. + */ + @ParameterizedTest(name = "pow({0}, {1}") + @MethodSource("testLogIntegerArguments") + public void pow_short_value(Integer v1, Integer v2) { + FunctionExpression pow = dsl.pow(DSL.literal(v1.shortValue()), DSL.literal(v2.shortValue())); + assertThat( + pow.valueOf(valueEnv()), + allOf(hasType(DOUBLE), hasValue(Math.pow(v1, v2)))); + assertEquals(String.format("pow(%s, %s)", v1, v2), pow.toString()); + + FunctionExpression power = + dsl.power(DSL.literal(v1.shortValue()), DSL.literal(v2.shortValue())); + assertThat( + power.valueOf(valueEnv()), + allOf(hasType(DOUBLE), hasValue(Math.pow(v1, v2)))); + assertEquals(String.format("pow(%s, %s)", v1, v2), pow.toString()); + } + /** * Test pow/power with integer value. */ diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/predicate/BinaryPredicateOperatorTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/predicate/BinaryPredicateOperatorTest.java index 92d35be900..d58830bf35 100644 --- a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/predicate/BinaryPredicateOperatorTest.java +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/predicate/BinaryPredicateOperatorTest.java @@ -30,13 +30,21 @@ import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.BOOLEAN; import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.INTEGER; import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.STRING; - import static com.amazon.opendistroforelasticsearch.sql.utils.ComparisonUtil.compare; import static com.amazon.opendistroforelasticsearch.sql.utils.OperatorUtils.matches; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprBooleanValue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprCollectionValue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprDoubleValue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprFloatValue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprIntegerValue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprLongValue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprShortValue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprStringValue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprTupleValue; import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValue; import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils; import com.amazon.opendistroforelasticsearch.sql.expression.DSL; @@ -68,12 +76,19 @@ private static Stream binaryPredicateArguments() { } private static Stream testEqualArguments() { - List arguments = Arrays.asList(1, 1L, 1F, 1D, "str", true, ImmutableList.of(1), - ImmutableMap.of("str", 1)); Stream.Builder builder = Stream.builder(); - for (Object argument : arguments) { - builder.add(Arguments.of(fromObjectValue(argument), fromObjectValue(argument))); - } + builder.add(Arguments.of(new ExprShortValue(1), new ExprShortValue(1))); + builder.add(Arguments.of(new ExprIntegerValue(1), new ExprIntegerValue(1))); + builder.add(Arguments.of(new ExprLongValue(1L), new ExprLongValue(1L))); + builder.add(Arguments.of(new ExprFloatValue(1F), new ExprFloatValue(1F))); + builder.add(Arguments.of(new ExprDoubleValue(1D), new ExprDoubleValue(1D))); + builder.add(Arguments.of(new ExprStringValue("str"), new ExprStringValue("str"))); + builder.add(Arguments.of(ExprBooleanValue.of(true), ExprBooleanValue.of(true))); + builder.add(Arguments.of(new ExprCollectionValue(ImmutableList.of(new ExprIntegerValue(1))), + new ExprCollectionValue(ImmutableList.of(new ExprIntegerValue(1))))); + builder.add(Arguments.of(ExprTupleValue.fromExprValueMap(ImmutableMap.of("str", + new ExprIntegerValue(1))), + ExprTupleValue.fromExprValueMap(ImmutableMap.of("str", new ExprIntegerValue(1))))); return builder.build(); } @@ -103,6 +118,9 @@ private static Stream testCompareValueArguments() { for (List argPair : arguments) { builder.add(Arguments.of(fromObjectValue(argPair.get(0)), fromObjectValue(argPair.get(1)))); } + builder.add(Arguments.of(new ExprShortValue(1), new ExprShortValue(1))); + builder.add(Arguments.of(new ExprShortValue(1), new ExprShortValue(2))); + builder.add(Arguments.of(new ExprShortValue(2), new ExprShortValue(1))); return builder.build(); } diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/utils/ComparisonUtil.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/utils/ComparisonUtil.java index 5c8155e75c..66197ef530 100644 --- a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/utils/ComparisonUtil.java +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/utils/ComparisonUtil.java @@ -25,6 +25,7 @@ import com.amazon.opendistroforelasticsearch.sql.data.model.ExprFloatValue; import com.amazon.opendistroforelasticsearch.sql.data.model.ExprIntegerValue; import com.amazon.opendistroforelasticsearch.sql.data.model.ExprLongValue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprShortValue; import com.amazon.opendistroforelasticsearch.sql.data.model.ExprStringValue; import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValue; import com.amazon.opendistroforelasticsearch.sql.exception.ExpressionEvaluationException; @@ -41,7 +42,9 @@ public static int compare(ExprValue v1, ExprValue v2) { throw new ExpressionEvaluationException("invalid to call compare operation on null value"); } - if (v1 instanceof ExprIntegerValue) { + if (v1 instanceof ExprShortValue) { + return v1.shortValue().compareTo(v2.shortValue()); + } else if (v1 instanceof ExprIntegerValue) { return getIntegerValue(v1).compareTo(getIntegerValue(v2)); } else if (v1 instanceof ExprLongValue) { return getLongValue(v1).compareTo(getLongValue(v2)); From ba19e6df7ffe0095bd83bf279c6e7676e760fbef Mon Sep 17 00:00:00 2001 From: penghuo Date: Tue, 25 Aug 2020 18:04:48 -0700 Subject: [PATCH 2/4] update: --- .../operator/arthmetic/ArithmeticFunction.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/ArithmeticFunction.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/ArithmeticFunction.java index 472be1814f..87f89ff228 100644 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/ArithmeticFunction.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/ArithmeticFunction.java @@ -144,13 +144,12 @@ private static FunctionResolver divide() { FunctionDSL.impl( FunctionDSL.nullMissingHandling( (v1, v2) -> v2.integerValue() == 0 ? ExprNullValue.of() : - new ExprIntegerValue(Math.floorDiv(v1.integerValue(), - v2.integerValue()))), + new ExprIntegerValue(v1.integerValue() / v2.integerValue())), INTEGER, INTEGER, INTEGER), FunctionDSL.impl( FunctionDSL.nullMissingHandling( (v1, v2) -> v2.longValue() == 0 ? ExprNullValue.of() : - new ExprLongValue(Math.floorDiv(v1.longValue(), v2.longValue()))), + new ExprLongValue(v1.longValue() / v2.longValue())), LONG, LONG, LONG), FunctionDSL.impl( FunctionDSL.nullMissingHandling( @@ -176,13 +175,12 @@ private static FunctionResolver modules() { FunctionDSL.impl( FunctionDSL.nullMissingHandling( (v1, v2) -> v2.integerValue() == 0 ? ExprNullValue.of() : - new ExprIntegerValue(Math.floorMod(v1.integerValue(), - v2.integerValue()))), + new ExprIntegerValue(v1.integerValue() % v2.integerValue())), INTEGER, INTEGER, INTEGER), FunctionDSL.impl( FunctionDSL.nullMissingHandling( (v1, v2) -> v2.longValue() == 0 ? ExprNullValue.of() : - new ExprLongValue(Math.floorMod(v1.longValue(), v2.longValue()))), + new ExprLongValue(v1.longValue() % v2.longValue())), LONG, LONG, LONG), FunctionDSL.impl( FunctionDSL.nullMissingHandling( From 78d3e3626556de15cbff975c86ae2a9601f328c9 Mon Sep 17 00:00:00 2001 From: penghuo Date: Wed, 26 Aug 2020 10:38:45 -0700 Subject: [PATCH 3/4] add branch coverage for core engine --- core/build.gradle | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index 53a66c1c10..f53c250869 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -49,9 +49,13 @@ jacocoTestCoverageVerification { violationRules { rule { limit { + counter = 'LINE' + minimum = 1.0 + } + limit { + counter = 'BRANCH' minimum = 1.0 } - } } afterEvaluate { From 8f1b35b40af24e394d507e5fe6263d3619504a39 Mon Sep 17 00:00:00 2001 From: penghuo Date: Thu, 27 Aug 2020 08:00:16 -0700 Subject: [PATCH 4/4] address comments --- .../arthmetic/MathematicalFunctionTest.java | 16 +++++++++++----- .../storage/ElasticsearchIndex.java | 3 --- .../script/filter/FilterQueryBuilder.java | 7 +------ .../storage/ElasticsearchIndexTest.java | 4 +--- .../script/filter/FilterQueryBuilderTest.java | 9 --------- 5 files changed, 13 insertions(+), 26 deletions(-) diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/MathematicalFunctionTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/MathematicalFunctionTest.java index 2fd8a99e8d..586b95b4bf 100644 --- a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/MathematicalFunctionTest.java +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/MathematicalFunctionTest.java @@ -57,6 +57,12 @@ @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) public class MathematicalFunctionTest extends ExpressionTestBase { + + private static Stream testLogShortArguments() { + Stream.Builder builder = Stream.builder(); + return builder.add(Arguments.of((short) 2, (short) 2)).build(); + } + private static Stream testLogIntegerArguments() { Stream.Builder builder = Stream.builder(); return builder.add(Arguments.of(2, 2)).build(); @@ -1183,19 +1189,19 @@ public void mod_null_missing() { } /** - * Test pow/power with integer value. + * Test pow/power with short value. */ @ParameterizedTest(name = "pow({0}, {1}") - @MethodSource("testLogIntegerArguments") - public void pow_short_value(Integer v1, Integer v2) { - FunctionExpression pow = dsl.pow(DSL.literal(v1.shortValue()), DSL.literal(v2.shortValue())); + @MethodSource("testLogShortArguments") + public void pow_short_value(Short v1, Short v2) { + FunctionExpression pow = dsl.pow(DSL.literal(v1), DSL.literal(v2)); assertThat( pow.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue(Math.pow(v1, v2)))); assertEquals(String.format("pow(%s, %s)", v1, v2), pow.toString()); FunctionExpression power = - dsl.power(DSL.literal(v1.shortValue()), DSL.literal(v2.shortValue())); + dsl.power(DSL.literal(v1), DSL.literal(v2)); assertThat( power.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue(Math.pow(v1, v2)))); diff --git a/elasticsearch/src/main/java/com/amazon/opendistroforelasticsearch/sql/elasticsearch/storage/ElasticsearchIndex.java b/elasticsearch/src/main/java/com/amazon/opendistroforelasticsearch/sql/elasticsearch/storage/ElasticsearchIndex.java index ec5ce33e63..19beeae465 100644 --- a/elasticsearch/src/main/java/com/amazon/opendistroforelasticsearch/sql/elasticsearch/storage/ElasticsearchIndex.java +++ b/elasticsearch/src/main/java/com/amazon/opendistroforelasticsearch/sql/elasticsearch/storage/ElasticsearchIndex.java @@ -106,9 +106,6 @@ public PhysicalPlan visitFilter(LogicalFilter node, ElasticsearchIndexScan conte new FilterQueryBuilder(new DefaultExpressionSerializer()); QueryBuilder query = queryBuilder.build(node.getCondition()); - if (query == null) { // Use default filter operator if unable to push down - return super.visitFilter(node, context); - } context.pushDown(query); return visitChild(node, context); diff --git a/elasticsearch/src/main/java/com/amazon/opendistroforelasticsearch/sql/elasticsearch/storage/script/filter/FilterQueryBuilder.java b/elasticsearch/src/main/java/com/amazon/opendistroforelasticsearch/sql/elasticsearch/storage/script/filter/FilterQueryBuilder.java index 79fca1982a..8146328a87 100644 --- a/elasticsearch/src/main/java/com/amazon/opendistroforelasticsearch/sql/elasticsearch/storage/script/filter/FilterQueryBuilder.java +++ b/elasticsearch/src/main/java/com/amazon/opendistroforelasticsearch/sql/elasticsearch/storage/script/filter/FilterQueryBuilder.java @@ -68,12 +68,7 @@ public class FilterQueryBuilder extends ExpressionNodeVisitor