From c316dfcc183255ba94a30dd551fa818143ec4a3a Mon Sep 17 00:00:00 2001 From: chloe-zh Date: Mon, 29 Jun 2020 13:10:54 -0700 Subject: [PATCH 01/11] Support ceil/ceiling, exact, exp, floor, ln, log functions --- .../sql/expression/DSL.java | 32 ++ .../expression/config/ExpressionConfig.java | 4 +- .../function/BuiltinFunctionName.java | 13 + .../expression/operator/OperatorUtils.java | 51 ++++ .../arthmetic/MathematicalFunction.java | 191 ++++++++++++ .../operator/arthmetic/UnaryFunction.java | 78 ----- .../sql/config/TestConfig.java | 4 + .../sql/expression/ExpressionTestBase.java | 4 + .../arthmetic/MathematicalFunctionTest.java | 283 ++++++++++++++++++ .../operator/arthmetic/UnaryFunctionTest.java | 102 ------- ppl/src/main/antlr/OpenDistroPPLLexer.g4 | 105 +------ ppl/src/main/antlr/OpenDistroPPLParser.g4 | 2 +- 12 files changed, 582 insertions(+), 287 deletions(-) create mode 100644 core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/MathematicalFunction.java delete mode 100644 core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/UnaryFunction.java create mode 100644 core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/MathematicalFunctionTest.java delete mode 100644 core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/UnaryFunctionTest.java 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 ec09af5628..63e13f872a 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 @@ -58,6 +58,38 @@ public FunctionExpression abs(Environment env, Expression. repository.compile(BuiltinFunctionName.ABS.getName(), Arrays.asList(expressions), env); } + public FunctionExpression ceil(Environment env, Expression... expressions) { + return (FunctionExpression) + repository.compile(BuiltinFunctionName.CEIL.getName(), Arrays.asList(expressions), env); + } + + public FunctionExpression ceiling( + Environment env, Expression... expressions) { + return (FunctionExpression) + repository.compile(BuiltinFunctionName.CEILING.getName(), Arrays.asList(expressions), env); + } + + public FunctionExpression exp(Environment env, Expression... expressions) { + return (FunctionExpression) + repository.compile(BuiltinFunctionName.EXP.getName(), Arrays.asList(expressions), env); + } + + public FunctionExpression floor( + Environment env, Expression... expressions) { + return (FunctionExpression) + repository.compile(BuiltinFunctionName.FLOOR.getName(), Arrays.asList(expressions), env); + } + + public FunctionExpression ln(Environment env, Expression... expressions) { + return (FunctionExpression) + repository.compile(BuiltinFunctionName.LN.getName(), Arrays.asList(expressions), env); + } + + public FunctionExpression log(Environment env, Expression... expressions) { + return (FunctionExpression) + repository.compile(BuiltinFunctionName.LOG.getName(), Arrays.asList(expressions), env); + } + public FunctionExpression add(Environment env, Expression... expressions) { return (FunctionExpression) repository.compile(BuiltinFunctionName.ADD.getName(), Arrays.asList(expressions), env); diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/config/ExpressionConfig.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/config/ExpressionConfig.java index da9d9ea1d1..716bf85e1b 100644 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/config/ExpressionConfig.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/config/ExpressionConfig.java @@ -19,7 +19,7 @@ import com.amazon.opendistroforelasticsearch.sql.expression.aggregation.AggregatorFunction; import com.amazon.opendistroforelasticsearch.sql.expression.function.BuiltinFunctionRepository; import com.amazon.opendistroforelasticsearch.sql.expression.operator.arthmetic.ArithmeticFunction; -import com.amazon.opendistroforelasticsearch.sql.expression.operator.arthmetic.UnaryFunction; +import com.amazon.opendistroforelasticsearch.sql.expression.operator.arthmetic.MathematicalFunction; import com.amazon.opendistroforelasticsearch.sql.expression.operator.predicate.BinaryPredicateOperator; import com.amazon.opendistroforelasticsearch.sql.expression.operator.predicate.UnaryPredicateOperator; import java.util.HashMap; @@ -40,7 +40,7 @@ public BuiltinFunctionRepository functionRepository() { new BuiltinFunctionRepository(new HashMap<>()); ArithmeticFunction.register(builtinFunctionRepository); BinaryPredicateOperator.register(builtinFunctionRepository); - UnaryFunction.register(builtinFunctionRepository); + MathematicalFunction.register(builtinFunctionRepository); UnaryPredicateOperator.register(builtinFunctionRepository); AggregatorFunction.register(builtinFunctionRepository); return builtinFunctionRepository; diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/BuiltinFunctionName.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/BuiltinFunctionName.java index 24bb3aafa5..190462dbd4 100644 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/BuiltinFunctionName.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/BuiltinFunctionName.java @@ -12,7 +12,20 @@ @Getter @RequiredArgsConstructor public enum BuiltinFunctionName { + /** + * Mathematical Functions. + */ ABS(FunctionName.of("abs")), + CEIL(FunctionName.of("ceil")), + CEILING(FunctionName.of("ceiling")), + EXP(FunctionName.of("exp")), + FLOOR(FunctionName.of("floor")), + LN(FunctionName.of("ln")), + LOG(FunctionName.of("log")), + + /** + * Text Functions. + */ TOSTRING(FunctionName.of("tostring")), /** 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 index 2fec6f77ea..0915f4d2aa 100644 --- 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 @@ -104,6 +104,57 @@ public String 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(Environment env) { + 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 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 new file mode 100644 index 0000000000..7ed434cc88 --- /dev/null +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/MathematicalFunction.java @@ -0,0 +1,191 @@ +/* + * 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.arthmetic; + +import static com.amazon.opendistroforelasticsearch.sql.expression.operator.OperatorUtils.binaryOperator; +import static com.amazon.opendistroforelasticsearch.sql.expression.operator.OperatorUtils.doubleArgFunc; +import static com.amazon.opendistroforelasticsearch.sql.expression.operator.OperatorUtils.unaryOperator; + +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprType; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils; +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.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 java.util.function.Function; +import lombok.experimental.UtilityClass; + +@UtilityClass +public class MathematicalFunction { + /** + * Register Mathematical Functions. + * + * @param repository {@link BuiltinFunctionRepository}. + */ + public static void register(BuiltinFunctionRepository repository) { + repository.register(abs()); + repository.register(ceil()); + repository.register(ceiling()); + repository.register(exp()); + repository.register(floor()); + repository.register(ln()); + repository.register(log()); + } + + /** + * Definition of abs() function. + * The supported signature of abs() function are + * INT -> INT + * LONG -> 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)); + } + + /** + * Definition of ceil(x)/ceiling(x) function. + * Calculate the next highest integer that x rounds up to + * The supported signature of ceil/ceiling function is + * DOUBLE -> DOUBLE + */ + private static FunctionResolver ceil() { + return new FunctionResolver(BuiltinFunctionName.CEIL.getName(), + singleArgumentFunction(BuiltinFunctionName.CEIL.getName(), Math::ceil)); + } + + private static FunctionResolver ceiling() { + return new FunctionResolver(BuiltinFunctionName.CEILING.getName(), + singleArgumentFunction(BuiltinFunctionName.CEILING.getName(), Math::ceil)); + } + + /** + * Definition of exp(x) function. + * Calculate exponent function e to the x + * The supported signature of exp function is + * DOUBLE -> DOUBLE + */ + private static FunctionResolver exp() { + return new FunctionResolver(BuiltinFunctionName.EXP.getName(), + singleArgumentFunction(BuiltinFunctionName.EXP.getName(), Math::exp)); + } + + /** + * Definition of floor(x) function. + * Calculate the next nearest whole integer that x rounds down to + * The supported signature of floor function is + * DOUBLE -> DOUBLE + */ + private static FunctionResolver floor() { + return new FunctionResolver(BuiltinFunctionName.FLOOR.getName(), + singleArgumentFunction(BuiltinFunctionName.FLOOR.getName(), Math::floor)); + } + + /** + * Definition of ln(x) function. + * Calculate the natural logarithm of x + * The supported signature of ln function is + * DOUBLE -> DOUBLE + */ + private static FunctionResolver ln() { + return new FunctionResolver(BuiltinFunctionName.LN.getName(), + singleArgumentFunction(BuiltinFunctionName.LN.getName(), Math::log)); + } + + /** + * Definition of log(x, y) function. + * Calculate the logarithm of x using y as the base + * The supported signature of log function is + * DOUBLE -> DOUBLE + */ + private static FunctionResolver log() { + return new FunctionResolver(BuiltinFunctionName.LOG.getName(), + doubleArgumentsFunction( + BuiltinFunctionName.LOG.getName(), (v1, v2) -> Math.log(v2) / Math.log(v1))); + } + + /** + * 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(ExprType.INTEGER)), + unaryOperator( + functionName, integerFunc, ExprValueUtils::getIntegerValue, ExprType.INTEGER)); + builder.put( + new FunctionSignature(functionName, Arrays.asList(ExprType.LONG)), + unaryOperator(functionName, longFunc, ExprValueUtils::getLongValue, ExprType.LONG)); + builder.put( + new FunctionSignature(functionName, Arrays.asList(ExprType.FLOAT)), + unaryOperator(functionName, floatFunc, ExprValueUtils::getFloatValue, ExprType.FLOAT)); + builder.put( + new FunctionSignature(functionName, Arrays.asList(ExprType.DOUBLE)), + unaryOperator(functionName, doubleFunc, ExprValueUtils::getDoubleValue, ExprType.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(ExprType.DOUBLE)), + unaryOperator( + functionName, doubleFunc, ExprValueUtils::getDoubleValue, ExprType.DOUBLE)) + .build(); + } + + /** + * Util method to generate single argument function bundles. Applicable for + * (DOUBLE, DOUBLE) -> DOUBLE + */ + private static Map doubleArgumentsFunction( + FunctionName functionName, + BiFunction doubleFunc) { + ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); + return builder + .put( + new FunctionSignature(functionName, Arrays.asList(ExprType.DOUBLE, ExprType.DOUBLE)), + doubleArgFunc(functionName, doubleFunc, ExprValueUtils::getDoubleValue, + ExprValueUtils::getDoubleValue, ExprType.DOUBLE)) + .build(); + } +} diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/UnaryFunction.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/UnaryFunction.java deleted file mode 100644 index 790f181753..0000000000 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/UnaryFunction.java +++ /dev/null @@ -1,78 +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.arthmetic; - -import static com.amazon.opendistroforelasticsearch.sql.expression.operator.OperatorUtils.unaryOperator; - -import com.amazon.opendistroforelasticsearch.sql.data.model.ExprType; -import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils; -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.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.Function; -import lombok.experimental.UtilityClass; - -@UtilityClass -public class UnaryFunction { - - public static void register(BuiltinFunctionRepository repository) { - repository.register(abs()); - } - - /** - * Definition of abs() function. - * The supported signature of abs() function are - * INT -> INT - * LONG -> LONG - * FLOAT -> FLOAT - * DOUBLE -> DOUBLE - */ - private static FunctionResolver abs() { - return new FunctionResolver( - BuiltinFunctionName.ABS.getName(), - unaryFunction( - BuiltinFunctionName.ABS.getName(), Math::abs, Math::abs, Math::abs, Math::abs)); - } - - private static Map unaryFunction( - FunctionName functionName, - Function integerFunc, - Function longFunc, - Function floatFunc, - Function doubleFunc) { - ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); - builder.put( - new FunctionSignature(functionName, Arrays.asList(ExprType.INTEGER)), - unaryOperator( - functionName, integerFunc, ExprValueUtils::getIntegerValue, ExprType.INTEGER)); - builder.put( - new FunctionSignature(functionName, Arrays.asList(ExprType.LONG)), - unaryOperator(functionName, longFunc, ExprValueUtils::getLongValue, ExprType.LONG)); - builder.put( - new FunctionSignature(functionName, Arrays.asList(ExprType.FLOAT)), - unaryOperator(functionName, floatFunc, ExprValueUtils::getFloatValue, ExprType.FLOAT)); - builder.put( - new FunctionSignature(functionName, Arrays.asList(ExprType.DOUBLE)), - unaryOperator(functionName, doubleFunc, ExprValueUtils::getDoubleValue, ExprType.DOUBLE)); - return builder.build(); - } -} diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/config/TestConfig.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/config/TestConfig.java index 1bbf6d2d98..8b0fa67f6b 100644 --- a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/config/TestConfig.java +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/config/TestConfig.java @@ -39,6 +39,8 @@ public class TestConfig { public static final String INT_TYPE_NULL_VALUE_FIELD = "int_null_value"; public static final String INT_TYPE_MISSING_VALUE_FIELD = "int_missing_value"; + public static final String DOUBLE_TYPE_NULL_VALUE_FIELD = "double_null_value"; + public static final String DOUBLE_TYPE_MISSING_VALUE_FIELD = "double_missing_value"; public static final String BOOL_TYPE_NULL_VALUE_FIELD = "null_value_boolean"; public static final String BOOL_TYPE_MISSING_VALUE_FIELD = "missing_value_boolean"; @@ -49,6 +51,8 @@ public class TestConfig { .put("long_value", ExprType.LONG) .put("float_value", ExprType.FLOAT) .put("double_value", ExprType.DOUBLE) + .put(DOUBLE_TYPE_NULL_VALUE_FIELD, ExprType.DOUBLE) + .put(DOUBLE_TYPE_MISSING_VALUE_FIELD, ExprType.DOUBLE) .put("boolean_value", ExprType.BOOLEAN) .put(BOOL_TYPE_NULL_VALUE_FIELD, ExprType.BOOLEAN) .put(BOOL_TYPE_MISSING_VALUE_FIELD, ExprType.BOOLEAN) diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/ExpressionTestBase.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/ExpressionTestBase.java index cb64d1d3fd..a89667e444 100644 --- a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/ExpressionTestBase.java +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/ExpressionTestBase.java @@ -17,6 +17,8 @@ import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.BOOL_TYPE_MISSING_VALUE_FIELD; import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.BOOL_TYPE_NULL_VALUE_FIELD; +import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.DOUBLE_TYPE_MISSING_VALUE_FIELD; +import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.DOUBLE_TYPE_NULL_VALUE_FIELD; import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.INT_TYPE_MISSING_VALUE_FIELD; import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.INT_TYPE_NULL_VALUE_FIELD; import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.booleanValue; @@ -82,9 +84,11 @@ protected Environment valueEnv() { return collectionValue(ImmutableList.of(1)); case BOOL_TYPE_NULL_VALUE_FIELD: case INT_TYPE_NULL_VALUE_FIELD: + case DOUBLE_TYPE_NULL_VALUE_FIELD: return nullValue(); case INT_TYPE_MISSING_VALUE_FIELD: case BOOL_TYPE_MISSING_VALUE_FIELD: + case DOUBLE_TYPE_MISSING_VALUE_FIELD: return missingValue(); default: throw new IllegalArgumentException("undefined reference"); 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 new file mode 100644 index 0000000000..659933384c --- /dev/null +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/MathematicalFunctionTest.java @@ -0,0 +1,283 @@ +/* + * 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.arthmetic; + +import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.DOUBLE_TYPE_MISSING_VALUE_FIELD; +import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.DOUBLE_TYPE_NULL_VALUE_FIELD; +import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.INT_TYPE_MISSING_VALUE_FIELD; +import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.INT_TYPE_NULL_VALUE_FIELD; +import static com.amazon.opendistroforelasticsearch.sql.utils.MatcherUtils.hasType; +import static com.amazon.opendistroforelasticsearch.sql.utils.MatcherUtils.hasValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprType; +import com.amazon.opendistroforelasticsearch.sql.expression.DSL; +import com.amazon.opendistroforelasticsearch.sql.expression.ExpressionTestBase; +import com.amazon.opendistroforelasticsearch.sql.expression.FunctionExpression; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +public class MathematicalFunctionTest extends ExpressionTestBase { + private static Stream testLogArguments() { + List> arguments = Arrays.asList( + Arrays.asList(2D, 2D), Arrays.asList(3D, 9D) + ); + Stream.Builder builder = Stream.builder(); + for (List argPair : arguments) { + builder.add(Arguments.of(argPair.get(0), argPair.get(1))); + } + return builder.build(); + } + + /** + * Test abs with integer value. + */ + @ParameterizedTest(name = "abs({0})") + @ValueSource(ints = {-2, 2}) + public void abs_int_value(Integer value) { + FunctionExpression abs = dsl.abs(typeEnv, DSL.literal(value)); + assertThat( + abs.valueOf(valueEnv()), + allOf(hasType(ExprType.INTEGER), hasValue(Math.abs(value)))); + assertEquals(String.format("abs(%s)", value.toString()), abs.toString()); + } + + /** + * Test abs with long value. + */ + @ParameterizedTest(name = "abs({0})") + @ValueSource(longs = {-2L, 2L}) + public void abs_long_value(Long value) { + FunctionExpression abs = dsl.abs(typeEnv, DSL.literal(value)); + assertThat( + abs.valueOf(valueEnv()), + allOf(hasType(ExprType.LONG), hasValue(Math.abs(value)))); + assertEquals(String.format("abs(%s)", value.toString()), abs.toString()); + } + + /** + * Test abs with float value. + */ + @ParameterizedTest(name = "abs({0})") + @ValueSource(floats = {-2f, 2f}) + public void abs_float_value(Float value) { + FunctionExpression abs = dsl.abs(typeEnv, DSL.literal(value)); + assertThat( + abs.valueOf(valueEnv()), + allOf(hasType(ExprType.FLOAT), hasValue(Math.abs(value)))); + assertEquals(String.format("abs(%s)", value.toString()), abs.toString()); + } + + /** + * Test abs with double value. + */ + @ParameterizedTest(name = "abs({0})") + @ValueSource(doubles = {-2L, 2L}) + public void abs_double_value(Double value) { + FunctionExpression abs = dsl.abs(typeEnv, DSL.literal(value)); + assertThat( + abs.valueOf(valueEnv()), + allOf(hasType(ExprType.DOUBLE), hasValue(Math.abs(value)))); + assertEquals(String.format("abs(%s)", value.toString()), abs.toString()); + } + + @Test + public void abs_null_value() { + assertTrue(dsl.abs(typeEnv, DSL.ref(INT_TYPE_NULL_VALUE_FIELD)).valueOf(valueEnv()).isNull()); + } + + @Test + public void abs_missing_value() { + assertTrue( + dsl.abs(typeEnv, DSL.ref(INT_TYPE_MISSING_VALUE_FIELD)).valueOf(valueEnv()).isMissing()); + } + + /** + * Test ceil/ceiling with double value. + */ + @ParameterizedTest(name = "ceil({0})") + @ValueSource(doubles = {-2L, 2L}) + public void ceil_double_value(Double value) { + FunctionExpression ceil = dsl.ceil(typeEnv, DSL.literal(value)); + assertEquals(ExprType.DOUBLE, ceil.type(typeEnv())); + assertThat( + ceil.valueOf(valueEnv()), + allOf(hasType(ExprType.DOUBLE), hasValue(Math.ceil(value)))); + assertEquals(String.format("ceil(%s)", value.toString()), ceil.toString()); + + FunctionExpression ceiling = dsl.ceiling(typeEnv, DSL.literal(value)); + assertEquals(ExprType.DOUBLE, ceiling.type(typeEnv())); + assertThat( + ceiling.valueOf(valueEnv()), + allOf(hasType(ExprType.DOUBLE), hasValue(Math.ceil(value)))); + assertEquals(String.format("ceiling(%s)", value.toString()), ceiling.toString()); + } + + @Test + public void ceil_null_value() { + assertTrue( + dsl.ceil(typeEnv, DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD)).valueOf(valueEnv()).isNull()); + assertTrue( + dsl.ceiling(typeEnv, DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD)).valueOf(valueEnv()).isNull()); + } + + @Test + public void ceil_missing_value() { + assertTrue(dsl.ceil( + typeEnv, DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD)).valueOf(valueEnv()).isMissing()); + assertTrue(dsl.ceiling( + typeEnv, DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD)).valueOf(valueEnv()).isMissing()); + } + + /** + * Test exp with double value. + */ + @ParameterizedTest(name = "exp({0})") + @ValueSource(doubles = {-2L, 2L}) + public void exp_double_value(Double value) { + FunctionExpression exp = dsl.exp(typeEnv, DSL.literal(value)); + assertEquals(ExprType.DOUBLE, exp.type(typeEnv())); + assertThat( + exp.valueOf(valueEnv()), + allOf(hasType(ExprType.DOUBLE), hasValue(Math.exp(value)))); + assertEquals(String.format("exp(%s)", value.toString()), exp.toString()); + } + + @Test + public void exp_null_value() { + assertTrue( + dsl.exp(typeEnv, DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD)).valueOf(valueEnv()).isNull()); + } + + @Test + public void exp_missing_value() { + assertTrue( + dsl.exp(typeEnv, DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD)).valueOf(valueEnv()).isMissing()); + } + + /** + * Test floor with double value. + */ + @ParameterizedTest(name = "floor({0})") + @ValueSource(doubles = {-2D, 2D}) + public void floor_double_value(Double value) { + FunctionExpression floor = dsl.floor(typeEnv, DSL.literal(value)); + assertEquals(ExprType.DOUBLE, floor.type(typeEnv())); + assertThat( + floor.valueOf(valueEnv()), + allOf(hasType(ExprType.DOUBLE), hasValue(Math.floor(value)))); + assertEquals(String.format("floor(%s)", value.toString()), floor.toString()); + } + + @Test + public void floor_null_value() { + assertTrue( + dsl.floor(typeEnv, DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD)).valueOf(valueEnv()).isNull()); + } + + @Test + public void floor_missing_value() { + assertTrue(dsl.floor( + typeEnv, DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD)).valueOf(valueEnv()).isMissing()); + } + + /** + * Test ln with double value. + */ + @ParameterizedTest(name = "ln({0})") + @ValueSource(doubles = {2D}) + public void ln_double_value(Double value) { + FunctionExpression ln = dsl.ln(typeEnv, DSL.literal(value)); + assertEquals(ExprType.DOUBLE, ln.type(typeEnv())); + assertThat( + ln.valueOf(valueEnv()), + allOf(hasType(ExprType.DOUBLE), hasValue(Math.log(value)))); + assertEquals(String.format("ln(%s)", value.toString()), ln.toString()); + } + + @Test + public void ln_null_value() { + assertTrue(dsl.ln(typeEnv, DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD)).valueOf(valueEnv()).isNull()); + } + + @Test + public void ln_missing_value() { + assertTrue( + dsl.ln(typeEnv, DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD)).valueOf(valueEnv()).isMissing()); + } + + /** + * Test log with double value. + */ + @ParameterizedTest(name = "log({0}, {1})") + @MethodSource("testLogArguments") + public void log_double_value(Double v1, Double v2) { + FunctionExpression log = dsl.log(typeEnv, DSL.literal(v1), DSL.literal(v2)); + assertEquals(ExprType.DOUBLE, log.type(typeEnv())); + assertThat( + log.valueOf(valueEnv()), + allOf(hasType(ExprType.DOUBLE), hasValue(Math.log(v2) / Math.log(v1)))); + assertEquals(String.format("log(%s, %s)", v1.toString(), v2.toString()), log.toString()); + } + + @Test + public void log_null_value() { + assertTrue(dsl.log(typeEnv, + DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD), + DSL.literal(2D)).valueOf(valueEnv()).isNull()); + assertTrue(dsl.log(typeEnv, + DSL.literal(2D), + DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD)).valueOf(valueEnv()).isNull()); + assertTrue(dsl.log(typeEnv, + DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD), + DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD)).valueOf(valueEnv()).isNull()); + } + + @Test + public void log_missing_value() { + assertTrue(dsl.log(typeEnv, + DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD), + DSL.literal(2D)).valueOf(valueEnv()).isMissing()); + assertTrue(dsl.log(typeEnv, + DSL.literal(2D), + DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD)).valueOf(valueEnv()).isMissing()); + assertTrue(dsl.log(typeEnv, + DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD), + DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD)).valueOf(valueEnv()).isMissing()); + } + + @Test + public void log_null_missing() { + assertTrue(dsl.log(typeEnv, + DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD), + DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD)).valueOf(valueEnv()).isMissing()); + assertTrue(dsl.log(typeEnv, + DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD), + DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD)).valueOf(valueEnv()).isMissing()); + } +} diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/UnaryFunctionTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/UnaryFunctionTest.java deleted file mode 100644 index 37366f51ae..0000000000 --- a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/UnaryFunctionTest.java +++ /dev/null @@ -1,102 +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.arthmetic; - -import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.INT_TYPE_MISSING_VALUE_FIELD; -import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.INT_TYPE_NULL_VALUE_FIELD; -import static com.amazon.opendistroforelasticsearch.sql.utils.MatcherUtils.hasType; -import static com.amazon.opendistroforelasticsearch.sql.utils.MatcherUtils.hasValue; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.allOf; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import com.amazon.opendistroforelasticsearch.sql.data.model.ExprType; -import com.amazon.opendistroforelasticsearch.sql.expression.DSL; -import com.amazon.opendistroforelasticsearch.sql.expression.ExpressionTestBase; -import com.amazon.opendistroforelasticsearch.sql.expression.FunctionExpression; -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) -public class UnaryFunctionTest extends ExpressionTestBase { - - /** - * Test abs with integer value. - */ - @ParameterizedTest(name = "abs({0})") - @ValueSource(ints = {-2, 2}) - public void abs_int_value(Integer value) { - FunctionExpression abs = dsl.abs(typeEnv, DSL.literal(value)); - assertThat( - abs.valueOf(valueEnv()), - allOf(hasType(ExprType.INTEGER), hasValue(Math.abs(value)))); - assertEquals(String.format("abs(%s)", value.toString()), abs.toString()); - } - - /** - * Test abs with long value. - */ - @ParameterizedTest(name = "abs({0})") - @ValueSource(longs = {-2L, 2L}) - public void abs_long_value(Long value) { - FunctionExpression abs = dsl.abs(typeEnv, DSL.literal(value)); - assertThat( - abs.valueOf(valueEnv()), - allOf(hasType(ExprType.LONG), hasValue(Math.abs(value)))); - assertEquals(String.format("abs(%s)", value.toString()), abs.toString()); - } - - /** - * Test abs with float value. - */ - @ParameterizedTest(name = "abs({0})") - @ValueSource(floats = {-2f, 2f}) - public void abs_float_value(Float value) { - FunctionExpression abs = dsl.abs(typeEnv, DSL.literal(value)); - assertThat( - abs.valueOf(valueEnv()), - allOf(hasType(ExprType.FLOAT), hasValue(Math.abs(value)))); - assertEquals(String.format("abs(%s)", value.toString()), abs.toString()); - } - - /** - * Test abs with double value. - */ - @ParameterizedTest(name = "abs({0})") - @ValueSource(doubles = {-2L, 2L}) - public void abs_double_value(Double value) { - FunctionExpression abs = dsl.abs(typeEnv, DSL.literal(value)); - assertThat( - abs.valueOf(valueEnv()), - allOf(hasType(ExprType.DOUBLE), hasValue(Math.abs(value)))); - assertEquals(String.format("abs(%s)", value.toString()), abs.toString()); - } - - @Test - public void abs_null_value() { - assertTrue(dsl.abs(typeEnv, DSL.ref(INT_TYPE_NULL_VALUE_FIELD)).valueOf(valueEnv()).isNull()); - } - - @Test - public void abs_missing_value() { - assertTrue( - dsl.abs(typeEnv, DSL.ref(INT_TYPE_MISSING_VALUE_FIELD)).valueOf(valueEnv()).isMissing()); - } -} diff --git a/ppl/src/main/antlr/OpenDistroPPLLexer.g4 b/ppl/src/main/antlr/OpenDistroPPLLexer.g4 index db6eac0404..e8fe6dc404 100644 --- a/ppl/src/main/antlr/OpenDistroPPLLexer.g4 +++ b/ppl/src/main/antlr/OpenDistroPPLLexer.g4 @@ -134,115 +134,12 @@ DC: 'DC'; // BASIC FUNCTIONS ABS: 'ABS'; -ACOS: 'ACOS'; -ADD: 'ADD'; -ADDDATE: 'ADDDATE'; -ADDTIME: 'ADDTIME'; -ASCII: 'ASCII'; -ASIN: 'ASIN'; -ATAN: 'ATAN'; -ATAN2: 'ATAN2'; -CBRT: 'CBRT'; CEIL: 'CEIL'; -CONCAT: 'CONCAT'; -CONCAT_WS: 'CONCAT_WS'; -COS: 'COS'; -COSH: 'COSH'; -COT: 'COT'; -CURDATE: 'CURDATE'; -DATE: 'DATE'; -DATE_FORMAT: 'DATE_FORMAT'; -DAYOFMONTH: 'DAYOFMONTH'; -DEGREES: 'DEGREES'; -E: 'E'; +CEILING: 'CEILING'; EXP: 'EXP'; -EXPM1: 'EXPM1'; FLOOR: 'FLOOR'; -GREATEST: 'GREATEST'; -IF: 'IF'; -IFNULL: 'IFNULL'; -ISNULL: 'ISNULL'; -LEAST: 'LEAST'; -LEFT: 'LEFT'; -LENGTH: 'LENGTH'; LN: 'LN'; -LOCATE: 'LOCATE'; LOG: 'LOG'; -LOG10: 'LOG10'; -LOG2: 'LOG2'; -LOWER: 'LOWER'; -LTRIM: 'LTRIM'; -MAKETIME: 'MAKETIME'; -MISSING: 'MISSING'; -MODULUS: 'MODULUS'; -MONTH: 'MONTH'; -MONTHNAME: 'MONTHNAME'; -MULTIPLY: 'MULTIPLY'; -NOW: 'NOW'; -PI: 'PI'; -POW: 'POW'; -POWER: 'POWER'; -RADIANS: 'RADIANS'; -RAND: 'RAND'; -REPLACE: 'REPLACE'; -RIGHT: 'RIGHT'; -RINT: 'RINT'; -ROUND: 'ROUND'; -RTRIM: 'RTRIM'; -SIGN: 'SIGN'; -SIGNUM: 'SIGNUM'; -SIN: 'SIN'; -SINH: 'SINH'; -SQRT: 'SQRT'; -SUBTRACT: 'SUBTRACT'; -SUBSTRING: 'SUBSTRING'; -TAN: 'TAN'; -TIMESTAMP: 'TIMESTAMP'; -TRIM: 'TRIM'; -UPPER: 'UPPER'; -YEAR: 'YEAR'; - -// ES FUNCTIONS -DATE_HISTOGRAM: 'DATE_HISTOGRAM'; -DAY_OF_MONTH: 'DAY_OF_MONTH'; -DAY_OF_YEAR: 'DAY_OF_YEAR'; -DAY_OF_WEEK: 'DAY_OF_WEEK'; -EXCLUDE: 'EXCLUDE'; -EXTENDED_STATS: 'EXTENDED_STATS'; -FIELD: 'FIELD'; -FILTER: 'FILTER'; -GEO_BOUNDING_BOX: 'GEO_BOUNDING_BOX'; -GEO_CELL: 'GEO_CELL'; -GEO_DISTANCE: 'GEO_DISTANCE'; -GEO_DISTANCE_RANGE: 'GEO_DISTANCE_RANGE'; -GEO_INTERSECTS: 'GEO_INTERSECTS'; -GEO_POLYGON: 'GEO_POLYGON'; -HISTOGRAM: 'HISTOGRAM'; -HOUR_OF_DAY: 'HOUR_OF_DAY'; -INCLUDE: 'INCLUDE'; -IN_TERMS: 'IN_TERMS'; -MATCHPHRASE: 'MATCHPHRASE'; -MATCH_PHRASE: 'MATCH_PHRASE'; -MATCHQUERY: 'MATCHQUERY'; -MATCH_QUERY: 'MATCH_QUERY'; -MINUTE_OF_DAY: 'MINUTE_OF_DAY'; -MINUTE_OF_HOUR: 'MINUTE_OF_HOUR'; -MONTH_OF_YEAR: 'MONTH_OF_YEAR'; -MULTIMATCH: 'MULTIMATCH'; -MULTI_MATCH: 'MULTI_MATCH'; -NESTED: 'NESTED'; -PERCENTILES: 'PERCENTILES'; -REGEXP_QUERY: 'REGEXP_QUERY'; -REVERSE_NESTED: 'REVERSE_NESTED'; -QUERY: 'QUERY'; -SCORE: 'SCORE'; -SECOND_OF_MINUTE: 'SECOND_OF_MINUTE'; -TERM: 'TERM'; -TERMS: 'TERMS'; -TOPHITS: 'TOPHITS'; -WEEK_OF_YEAR: 'WEEK_OF_YEAR'; -WILDCARDQUERY: 'WILDCARDQUERY'; -WILDCARD_QUERY: 'WILDCARD_QUERY'; // LITERALS AND VALUES //STRING_LITERAL: DQUOTA_STRING | SQUOTA_STRING | BQUOTA_STRING; diff --git a/ppl/src/main/antlr/OpenDistroPPLParser.g4 b/ppl/src/main/antlr/OpenDistroPPLParser.g4 index 28a99ecbda..cb642acdd2 100644 --- a/ppl/src/main/antlr/OpenDistroPPLParser.g4 +++ b/ppl/src/main/antlr/OpenDistroPPLParser.g4 @@ -203,7 +203,7 @@ functionArg ; mathematicalFunctionBase - : ABS + : ABS | CEIL | CEILING | EXP | FLOOR | LN | LOG ; dateAndTimeFunctionBase From 69ed20c556d36e9f874bd1b290465895d9486fec Mon Sep 17 00:00:00 2001 From: chloe-zh Date: Mon, 29 Jun 2020 15:56:32 -0700 Subject: [PATCH 02/11] Added integ test cases --- .../arthmetic/MathematicalFunction.java | 43 ++++-- .../arthmetic/MathematicalFunctionTest.java | 123 ++++++++++-------- .../sql/ppl/MathematicalFunctionIT.java | 109 ++++++++++++++++ 3 files changed, 212 insertions(+), 63 deletions(-) create mode 100644 integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/ppl/MathematicalFunctionIT.java 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 7ed434cc88..ad22e3e707 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,7 +15,6 @@ package com.amazon.opendistroforelasticsearch.sql.expression.operator.arthmetic; -import static com.amazon.opendistroforelasticsearch.sql.expression.operator.OperatorUtils.binaryOperator; import static com.amazon.opendistroforelasticsearch.sql.expression.operator.OperatorUtils.doubleArgFunc; import static com.amazon.opendistroforelasticsearch.sql.expression.operator.OperatorUtils.unaryOperator; @@ -70,16 +69,32 @@ private static FunctionResolver abs() { * Definition of ceil(x)/ceiling(x) function. * Calculate the next highest integer that x rounds up to * The supported signature of ceil/ceiling function is - * DOUBLE -> DOUBLE + * DOUBLE -> LONG */ private static FunctionResolver ceil() { - return new FunctionResolver(BuiltinFunctionName.CEIL.getName(), - singleArgumentFunction(BuiltinFunctionName.CEIL.getName(), Math::ceil)); + FunctionName functionName = BuiltinFunctionName.CEIL.getName(); + return new FunctionResolver( + BuiltinFunctionName.CEIL.getName(), + new ImmutableMap.Builder() + .put( + new FunctionSignature(functionName, Arrays.asList(ExprType.DOUBLE)), + unaryOperator( + functionName, v -> ((long) Math.ceil(v)), ExprValueUtils::getDoubleValue, + ExprType.LONG)) + .build()); } private static FunctionResolver ceiling() { - return new FunctionResolver(BuiltinFunctionName.CEILING.getName(), - singleArgumentFunction(BuiltinFunctionName.CEILING.getName(), Math::ceil)); + FunctionName functionName = BuiltinFunctionName.CEILING.getName(); + return new FunctionResolver( + BuiltinFunctionName.CEILING.getName(), + new ImmutableMap.Builder() + .put( + new FunctionSignature(functionName, Arrays.asList(ExprType.DOUBLE)), + unaryOperator( + functionName, v -> ((long) Math.ceil(v)), ExprValueUtils::getDoubleValue, + ExprType.LONG)) + .build()); } /** @@ -97,11 +112,19 @@ private static FunctionResolver exp() { * Definition of floor(x) function. * Calculate the next nearest whole integer that x rounds down to * The supported signature of floor function is - * DOUBLE -> DOUBLE + * DOUBLE -> LONG */ private static FunctionResolver floor() { - return new FunctionResolver(BuiltinFunctionName.FLOOR.getName(), - singleArgumentFunction(BuiltinFunctionName.FLOOR.getName(), Math::floor)); + FunctionName functionName = BuiltinFunctionName.FLOOR.getName(); + return new FunctionResolver( + BuiltinFunctionName.FLOOR.getName(), + new ImmutableMap.Builder() + .put( + new FunctionSignature(functionName, Arrays.asList(ExprType.DOUBLE)), + unaryOperator( + functionName, v -> ((long) Math.floor(v)), ExprValueUtils::getDoubleValue, + ExprType.LONG)) + .build()); } /** @@ -159,7 +182,7 @@ private static Map singleArgumentFunction( /** * Util method to generate single argument function bundles. Applicable for - * DOUBLE -> DOUBLE + * DOUBLE -> DOUBLE/LONG */ private static Map singleArgumentFunction( FunctionName functionName, 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 659933384c..022540e3aa 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 @@ -124,34 +124,37 @@ public void abs_missing_value() { @ValueSource(doubles = {-2L, 2L}) public void ceil_double_value(Double value) { FunctionExpression ceil = dsl.ceil(typeEnv, DSL.literal(value)); - assertEquals(ExprType.DOUBLE, ceil.type(typeEnv())); assertThat( - ceil.valueOf(valueEnv()), - allOf(hasType(ExprType.DOUBLE), hasValue(Math.ceil(value)))); + ceil.valueOf(valueEnv()), allOf(hasType(ExprType.LONG), hasValue((long) Math.ceil(value)))); assertEquals(String.format("ceil(%s)", value.toString()), ceil.toString()); FunctionExpression ceiling = dsl.ceiling(typeEnv, DSL.literal(value)); - assertEquals(ExprType.DOUBLE, ceiling.type(typeEnv())); assertThat( ceiling.valueOf(valueEnv()), - allOf(hasType(ExprType.DOUBLE), hasValue(Math.ceil(value)))); + allOf(hasType(ExprType.LONG), hasValue((long) Math.ceil(value)))); assertEquals(String.format("ceiling(%s)", value.toString()), ceiling.toString()); } @Test public void ceil_null_value() { - assertTrue( - dsl.ceil(typeEnv, DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD)).valueOf(valueEnv()).isNull()); - assertTrue( - dsl.ceiling(typeEnv, DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD)).valueOf(valueEnv()).isNull()); + FunctionExpression ceil = dsl.ceil(typeEnv(), DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD)); + assertEquals(ExprType.LONG, ceil.type(typeEnv())); + assertTrue(ceil.valueOf(valueEnv()).isNull()); + + FunctionExpression ceiling = dsl.ceiling(typeEnv(), DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD)); + assertEquals(ExprType.LONG, ceiling.type(typeEnv())); + assertTrue(ceiling.valueOf(valueEnv()).isNull()); } @Test public void ceil_missing_value() { - assertTrue(dsl.ceil( - typeEnv, DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD)).valueOf(valueEnv()).isMissing()); - assertTrue(dsl.ceiling( - typeEnv, DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD)).valueOf(valueEnv()).isMissing()); + FunctionExpression ceil = dsl.ceil(typeEnv, DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD)); + assertEquals(ExprType.LONG, ceil.type(typeEnv())); + assertTrue(ceil.valueOf(valueEnv()).isMissing()); + + ceil = dsl.ceiling(typeEnv, DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD)); + assertEquals(ExprType.LONG, ceil.type(typeEnv())); + assertTrue(ceil.valueOf(valueEnv()).isMissing()); } /** @@ -161,7 +164,6 @@ public void ceil_missing_value() { @ValueSource(doubles = {-2L, 2L}) public void exp_double_value(Double value) { FunctionExpression exp = dsl.exp(typeEnv, DSL.literal(value)); - assertEquals(ExprType.DOUBLE, exp.type(typeEnv())); assertThat( exp.valueOf(valueEnv()), allOf(hasType(ExprType.DOUBLE), hasValue(Math.exp(value)))); @@ -170,14 +172,16 @@ public void exp_double_value(Double value) { @Test public void exp_null_value() { - assertTrue( - dsl.exp(typeEnv, DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD)).valueOf(valueEnv()).isNull()); + FunctionExpression exp = dsl.exp(typeEnv, DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD)); + assertEquals(ExprType.DOUBLE, exp.type(typeEnv())); + assertTrue(exp.valueOf(valueEnv()).isNull()); } @Test public void exp_missing_value() { - assertTrue( - dsl.exp(typeEnv, DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD)).valueOf(valueEnv()).isMissing()); + FunctionExpression exp = dsl.exp(typeEnv, DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD)); + assertEquals(ExprType.DOUBLE, exp.type(typeEnv())); + assertTrue(exp.valueOf(valueEnv()).isMissing()); } /** @@ -187,23 +191,24 @@ public void exp_missing_value() { @ValueSource(doubles = {-2D, 2D}) public void floor_double_value(Double value) { FunctionExpression floor = dsl.floor(typeEnv, DSL.literal(value)); - assertEquals(ExprType.DOUBLE, floor.type(typeEnv())); assertThat( floor.valueOf(valueEnv()), - allOf(hasType(ExprType.DOUBLE), hasValue(Math.floor(value)))); + allOf(hasType(ExprType.LONG), hasValue(((long) Math.floor(value))))); assertEquals(String.format("floor(%s)", value.toString()), floor.toString()); } @Test public void floor_null_value() { - assertTrue( - dsl.floor(typeEnv, DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD)).valueOf(valueEnv()).isNull()); + FunctionExpression floor = dsl.floor(typeEnv, DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD)); + assertEquals(ExprType.LONG, floor.type(typeEnv())); + assertTrue(floor.valueOf(valueEnv()).isNull()); } @Test public void floor_missing_value() { - assertTrue(dsl.floor( - typeEnv, DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD)).valueOf(valueEnv()).isMissing()); + FunctionExpression floor = dsl.floor(typeEnv, DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD)); + assertEquals(ExprType.LONG, floor.type(typeEnv())); + assertTrue(floor.valueOf(valueEnv()).isMissing()); } /** @@ -213,7 +218,6 @@ public void floor_missing_value() { @ValueSource(doubles = {2D}) public void ln_double_value(Double value) { FunctionExpression ln = dsl.ln(typeEnv, DSL.literal(value)); - assertEquals(ExprType.DOUBLE, ln.type(typeEnv())); assertThat( ln.valueOf(valueEnv()), allOf(hasType(ExprType.DOUBLE), hasValue(Math.log(value)))); @@ -222,13 +226,16 @@ public void ln_double_value(Double value) { @Test public void ln_null_value() { - assertTrue(dsl.ln(typeEnv, DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD)).valueOf(valueEnv()).isNull()); + FunctionExpression ln = dsl.ln(typeEnv, DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD)); + assertEquals(ExprType.DOUBLE, ln.type(typeEnv())); + assertTrue(ln.valueOf(valueEnv()).isNull()); } @Test public void ln_missing_value() { - assertTrue( - dsl.ln(typeEnv, DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD)).valueOf(valueEnv()).isMissing()); + FunctionExpression ln = dsl.ln(typeEnv, DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD)); + assertEquals(ExprType.DOUBLE, ln.type(typeEnv())); + assertTrue(ln.valueOf(valueEnv()).isMissing()); } /** @@ -238,7 +245,6 @@ public void ln_missing_value() { @MethodSource("testLogArguments") public void log_double_value(Double v1, Double v2) { FunctionExpression log = dsl.log(typeEnv, DSL.literal(v1), DSL.literal(v2)); - assertEquals(ExprType.DOUBLE, log.type(typeEnv())); assertThat( log.valueOf(valueEnv()), allOf(hasType(ExprType.DOUBLE), hasValue(Math.log(v2) / Math.log(v1)))); @@ -247,37 +253,48 @@ public void log_double_value(Double v1, Double v2) { @Test public void log_null_value() { - assertTrue(dsl.log(typeEnv, - DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD), - DSL.literal(2D)).valueOf(valueEnv()).isNull()); - assertTrue(dsl.log(typeEnv, - DSL.literal(2D), - DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD)).valueOf(valueEnv()).isNull()); - assertTrue(dsl.log(typeEnv, - DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD), - DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD)).valueOf(valueEnv()).isNull()); + FunctionExpression log = dsl.log( + typeEnv, DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD), DSL.literal(2D)); + assertEquals(ExprType.DOUBLE, log.type(typeEnv())); + assertTrue(log.valueOf(valueEnv()).isNull()); + + log = dsl.log(typeEnv, DSL.literal(2D), DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD)); + assertEquals(ExprType.DOUBLE, log.type(typeEnv())); + assertTrue(log.valueOf(valueEnv()).isNull()); + + log = dsl.log( + typeEnv, DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD), DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD)); + assertEquals(ExprType.DOUBLE, log.type(typeEnv())); + assertTrue(log.valueOf(valueEnv()).isNull()); } @Test public void log_missing_value() { - assertTrue(dsl.log(typeEnv, - DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD), - DSL.literal(2D)).valueOf(valueEnv()).isMissing()); - assertTrue(dsl.log(typeEnv, - DSL.literal(2D), - DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD)).valueOf(valueEnv()).isMissing()); - assertTrue(dsl.log(typeEnv, - DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD), - DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD)).valueOf(valueEnv()).isMissing()); + FunctionExpression log = dsl.log( + typeEnv, DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD), DSL.literal(2D)); + assertEquals(ExprType.DOUBLE, log.type(typeEnv())); + assertTrue(log.valueOf(valueEnv()).isMissing()); + + log = dsl.log(typeEnv, DSL.literal(2D), DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD)); + assertEquals(ExprType.DOUBLE, log.type(typeEnv())); + assertTrue(log.valueOf(valueEnv()).isMissing()); + + log = dsl.log(typeEnv, + DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD), DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD)); + assertEquals(ExprType.DOUBLE, log.type(typeEnv())); + assertTrue(log.valueOf(valueEnv()).isMissing()); } @Test public void log_null_missing() { - assertTrue(dsl.log(typeEnv, - DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD), - DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD)).valueOf(valueEnv()).isMissing()); - assertTrue(dsl.log(typeEnv, - DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD), - DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD)).valueOf(valueEnv()).isMissing()); + FunctionExpression log = dsl.log( + typeEnv, DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD), DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD)); + assertEquals(ExprType.DOUBLE, log.type(typeEnv())); + assertTrue(log.valueOf(valueEnv()).isMissing()); + + log = dsl.log( + typeEnv, DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD), DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD)); + assertEquals(ExprType.DOUBLE, log.type(typeEnv())); + assertTrue(log.valueOf(valueEnv()).isMissing()); } } diff --git a/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/ppl/MathematicalFunctionIT.java b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/ppl/MathematicalFunctionIT.java new file mode 100644 index 0000000000..443dd520c3 --- /dev/null +++ b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/ppl/MathematicalFunctionIT.java @@ -0,0 +1,109 @@ +/* + * 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.ppl; + +import static com.amazon.opendistroforelasticsearch.sql.legacy.TestsConstants.TEST_INDEX_BANK; +import static com.amazon.opendistroforelasticsearch.sql.util.MatcherUtils.rows; +import static com.amazon.opendistroforelasticsearch.sql.util.MatcherUtils.schema; +import static com.amazon.opendistroforelasticsearch.sql.util.MatcherUtils.verifyDataRows; +import static com.amazon.opendistroforelasticsearch.sql.util.MatcherUtils.verifySchema; + +import java.io.IOException; +import org.json.JSONObject; +import org.junit.jupiter.api.Test; + +public class MathematicalFunctionIT extends PPLIntegTestCase { + + @Override + public void init() throws IOException { + loadIndex(Index.BANK); + loadIndex(Index.BANK_WITH_NULL_VALUES); + } + + @Test + public void testAbs() throws IOException { + JSONObject result = + executeQuery( + String.format( + "source=%s | eval f = abs(age) | fields f", TEST_INDEX_BANK)); + verifySchema(result, schema("f", null, "integer")); + verifyDataRows( + result, + rows(32), rows(36), rows(28), rows(33), rows(36), rows(39), rows(34)); + } + + @Test + public void testCeil() throws IOException { + JSONObject result = + executeQuery( + String.format( + "source=%s | eval f = ceil(age) | fields f", TEST_INDEX_BANK)); + verifySchema(result, schema("f", null, "long")); + verifyDataRows( + result, + rows(32), rows(36), rows(28), rows(33), rows(36), rows(39), rows(34)); + } + + @Test + public void testExp() throws IOException { + JSONObject result = + executeQuery( + String.format( + "source=%s | eval f = exp(age) | fields f", TEST_INDEX_BANK)); + verifySchema(result, schema("f", null, "double")); + verifyDataRows( + result, rows(Math.exp(32)), rows(Math.exp(36)), rows(Math.exp(28)), rows(Math.exp(33)), + rows(Math.exp(36)), rows(Math.exp(39)), rows(Math.exp(34))); + } + + @Test + public void testFloor() throws IOException { + JSONObject result = + executeQuery( + String.format( + "source=%s | eval f = floor(age) | fields f", TEST_INDEX_BANK)); + verifySchema(result, schema("f", null, "long")); + verifyDataRows( + result, + rows(32), rows(36), rows(28), rows(33), rows(36), rows(39), rows(34)); + } + + @Test + public void testLn() throws IOException { + JSONObject result = + executeQuery( + String.format( + "source=%s | eval f = ln(age) | fields f", TEST_INDEX_BANK)); + verifySchema(result, schema("f", null, "double")); + verifyDataRows( + result, rows(Math.log(32)), rows(Math.log(36)), rows(Math.log(28)), rows(Math.log(33)), + rows(Math.log(36)), rows(Math.log(39)), rows(Math.log(34))); + } + + @Test + public void testLog() throws IOException { + JSONObject result = + executeQuery( + String.format( + "source=%s | eval f = log(age, balance) | fields f", TEST_INDEX_BANK)); + verifySchema(result, schema("f", null, "double")); + verifyDataRows( + result, rows(Math.log(39225) / Math.log(32)), rows(Math.log(5686) / Math.log(36)), + rows(Math.log(32838) / Math.log(28)), rows(Math.log(4180) / Math.log(33)), + rows(Math.log(16418) / Math.log(36)), rows(Math.log(40540) / Math.log(39)), + rows(Math.log(48086) / Math.log(34))); + } +} From 03d932da33dc7879edd49ff2bb7d903939908176 Mon Sep 17 00:00:00 2001 From: chloe-zh Date: Wed, 8 Jul 2020 12:57:28 -0700 Subject: [PATCH 03/11] Set the registration of function name to be case insensitive --- .../sql/expression/function/FunctionName.java | 2 +- .../sql/expression/function/BuiltinFunctionNameTest.java | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/FunctionName.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/FunctionName.java index a3611a0506..25c865dbb7 100644 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/FunctionName.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/FunctionName.java @@ -29,7 +29,7 @@ public class FunctionName { private final String functionName; public static FunctionName of(String functionName) { - return new FunctionName(functionName); + return new FunctionName(functionName.toLowerCase()); } @Override diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/function/BuiltinFunctionNameTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/function/BuiltinFunctionNameTest.java index 6feeb831b4..f73ec06812 100644 --- a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/function/BuiltinFunctionNameTest.java +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/function/BuiltinFunctionNameTest.java @@ -20,6 +20,7 @@ import java.util.Arrays; import java.util.stream.Stream; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -39,4 +40,10 @@ public void of(String name, BuiltinFunctionName expected) { assertTrue(BuiltinFunctionName.of(name).isPresent()); assertEquals(expected, BuiltinFunctionName.of(name).get()); } + + @Test + public void caseInsensitive() { + assertTrue(BuiltinFunctionName.of("aBs").isPresent()); + assertEquals(BuiltinFunctionName.of("aBs").get(), BuiltinFunctionName.ABS); + } } \ No newline at end of file From 4ebd3608758b27c8cdc1f934a11d67b9fa5fa394 Mon Sep 17 00:00:00 2001 From: chloe-zh Date: Wed, 8 Jul 2020 21:28:35 -0700 Subject: [PATCH 04/11] Function signature changed consistent with mysql --- .../sql/expression/DSL.java | 5 - .../function/BuiltinFunctionName.java | 1 - .../arthmetic/MathematicalFunction.java | 66 ++--- .../arthmetic/MathematicalFunctionTest.java | 250 ++++++++++++++++-- .../sql/ppl/MathematicalFunctionIT.java | 4 +- ppl/src/main/antlr/OpenDistroPPLLexer.g4 | 1 - ppl/src/main/antlr/OpenDistroPPLParser.g4 | 2 +- 7 files changed, 254 insertions(+), 75 deletions(-) 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 6533e88dbc..f13fab346b 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 @@ -71,11 +71,6 @@ public FunctionExpression ceil(Expression... expressions) { repository.compile(BuiltinFunctionName.CEIL.getName(), Arrays.asList(expressions)); } - public FunctionExpression ceiling(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.CEILING.getName(), Arrays.asList(expressions)); - } - public FunctionExpression exp(Expression... expressions) { return (FunctionExpression) repository.compile(BuiltinFunctionName.EXP.getName(), Arrays.asList(expressions)); diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/BuiltinFunctionName.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/BuiltinFunctionName.java index 190462dbd4..2d2c6d0898 100644 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/BuiltinFunctionName.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/BuiltinFunctionName.java @@ -17,7 +17,6 @@ public enum BuiltinFunctionName { */ ABS(FunctionName.of("abs")), CEIL(FunctionName.of("ceil")), - CEILING(FunctionName.of("ceiling")), EXP(FunctionName.of("exp")), FLOOR(FunctionName.of("floor")), LN(FunctionName.of("ln")), 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 c76eb6dfb4..3f657cda88 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 @@ -43,7 +43,6 @@ public class MathematicalFunction { public static void register(BuiltinFunctionRepository repository) { repository.register(abs()); repository.register(ceil()); - repository.register(ceiling()); repository.register(exp()); repository.register(floor()); repository.register(ln()); @@ -66,42 +65,32 @@ private static FunctionResolver abs() { } /** - * Definition of ceil(x)/ceiling(x) function. + * Definition of ceil(x) function. * Calculate the next highest integer that x rounds up to * The supported signature of ceil/ceiling function is - * DOUBLE -> LONG + * INTEGER -> INTEGER + * LONG -> LONG + * FLOAT -> FLOAT + * DOUBLE -> DOUBLE */ private static FunctionResolver ceil() { FunctionName functionName = BuiltinFunctionName.CEIL.getName(); return new FunctionResolver( - BuiltinFunctionName.CEIL.getName(), - new ImmutableMap.Builder() - .put( - new FunctionSignature(functionName, Arrays.asList(ExprCoreType.DOUBLE)), - unaryOperator( - functionName, v -> ((long) Math.ceil(v)), ExprValueUtils::getDoubleValue, - ExprCoreType.LONG)) - .build()); - } - - private static FunctionResolver ceiling() { - FunctionName functionName = BuiltinFunctionName.CEILING.getName(); - return new FunctionResolver( - BuiltinFunctionName.CEILING.getName(), - new ImmutableMap.Builder() - .put( - new FunctionSignature(functionName, Arrays.asList(ExprCoreType.DOUBLE)), - unaryOperator( - functionName, v -> ((long) Math.ceil(v)), ExprValueUtils::getDoubleValue, - ExprCoreType.LONG)) - .build()); + functionName, + singleArgumentFunction( + functionName, + v -> (int) Math.ceil(v), + v -> (long) Math.ceil(v), + v -> (float) Math.ceil(v), + Math::ceil + )); } /** * Definition of exp(x) function. * Calculate exponent function e to the x * The supported signature of exp function is - * DOUBLE -> DOUBLE + * INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE */ private static FunctionResolver exp() { return new FunctionResolver(BuiltinFunctionName.EXP.getName(), @@ -112,26 +101,29 @@ private static FunctionResolver exp() { * Definition of floor(x) function. * Calculate the next nearest whole integer that x rounds down to * The supported signature of floor function is - * DOUBLE -> LONG + * INTEGER -> INTEGER + * LONG -> LONG + * FLOAT -> FLOAT + * DOUBLE -> DOUBLE */ private static FunctionResolver floor() { FunctionName functionName = BuiltinFunctionName.FLOOR.getName(); return new FunctionResolver( - BuiltinFunctionName.FLOOR.getName(), - new ImmutableMap.Builder() - .put( - new FunctionSignature(functionName, Arrays.asList(ExprCoreType.DOUBLE)), - unaryOperator( - functionName, v -> ((long) Math.floor(v)), ExprValueUtils::getDoubleValue, - ExprCoreType.LONG)) - .build()); + functionName, + singleArgumentFunction( + functionName, + v -> (int) Math.floor(v), + v -> (long) Math.floor(v), + v -> (float) Math.floor(v), + Math::floor + )); } /** * Definition of ln(x) function. * Calculate the natural logarithm of x * The supported signature of ln function is - * DOUBLE -> DOUBLE + * INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE */ private static FunctionResolver ln() { return new FunctionResolver(BuiltinFunctionName.LN.getName(), @@ -142,7 +134,7 @@ private static FunctionResolver ln() { * Definition of log(x, y) function. * Calculate the logarithm of x using y as the base * The supported signature of log function is - * DOUBLE -> DOUBLE + * (x: INTEGER/LONG/FLOAT/DOUBLE, y: INTEGER/LONG/FLOAT/DOUBLE) -> DOUBLE */ private static FunctionResolver log() { return new FunctionResolver(BuiltinFunctionName.LOG.getName(), @@ -183,7 +175,7 @@ private static Map singleArgumentFunction( /** * Util method to generate single argument function bundles. Applicable for - * DOUBLE -> DOUBLE/LONG + * DOUBLE (INTEGER/LONG/FLOAT) -> DOUBLE */ private static Map singleArgumentFunction( FunctionName functionName, 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 3cfb86b4f7..413fb8c5ad 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 @@ -19,6 +19,7 @@ import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.DOUBLE_TYPE_NULL_VALUE_FIELD; import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.INT_TYPE_MISSING_VALUE_FIELD; import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.INT_TYPE_NULL_VALUE_FIELD; +import static com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils.getDoubleValue; 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; @@ -27,6 +28,7 @@ import static com.amazon.opendistroforelasticsearch.sql.utils.MatcherUtils.hasValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.closeTo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -46,15 +48,24 @@ @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) public class MathematicalFunctionTest extends ExpressionTestBase { - private static Stream testLogArguments() { - List> arguments = Arrays.asList( - Arrays.asList(2D, 2D), Arrays.asList(3D, 9D) - ); + private static Stream testLogIntegerArguments() { Stream.Builder builder = Stream.builder(); - for (List argPair : arguments) { - builder.add(Arguments.of(argPair.get(0), argPair.get(1))); - } - return builder.build(); + return builder.add(Arguments.of(2, 2)).build(); + } + + private static Stream testLogLongArguments() { + Stream.Builder builder = Stream.builder(); + return builder.add(Arguments.of(2L, 2L)).build(); + } + + private static Stream testLogFloatArguments() { + Stream.Builder builder = Stream.builder(); + return builder.add(Arguments.of(2F, 2F)).build(); + } + + private static Stream testLogDoubleArguments() { + Stream.Builder builder = Stream.builder(); + return builder.add(Arguments.of(2D, 2D)).build(); } /** @@ -120,22 +131,53 @@ public void abs_missing_value() { dsl.abs(DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER)).valueOf(valueEnv()).isMissing()); } + + /** + * Test ceil with integer value. + */ + @ParameterizedTest(name = "ceil({0})") + @ValueSource(ints = {2, -2}) + public void ceil_int_value(Integer value) { + FunctionExpression ceil = dsl.ceil(DSL.literal(value)); + assertThat( + ceil.valueOf(valueEnv()), allOf(hasType(INTEGER), hasValue((int) Math.ceil(value)))); + assertEquals(String.format("ceil(%s)", value.toString()), ceil.toString()); + } + /** - * Test ceil/ceiling with double value. + * Test ceil with long value. */ @ParameterizedTest(name = "ceil({0})") - @ValueSource(doubles = {-2L, 2L}) - public void ceil_double_value(Double value) { + @ValueSource(longs = {2L, -2L}) + public void ceil_long_value(Long value) { FunctionExpression ceil = dsl.ceil(DSL.literal(value)); assertThat( ceil.valueOf(valueEnv()), allOf(hasType(LONG), hasValue((long) Math.ceil(value)))); assertEquals(String.format("ceil(%s)", value.toString()), ceil.toString()); + } + + /** + * Test ceil with float value. + */ + @ParameterizedTest(name = "ceil({0})") + @ValueSource(floats = {2F, -2F}) + public void ceil_float_value(Float value) { + FunctionExpression ceil = dsl.ceil(DSL.literal(value)); + assertThat( + ceil.valueOf(valueEnv()), allOf(hasType(FLOAT), hasValue((float) Math.ceil(value)))); + assertEquals(String.format("ceil(%s)", value.toString()), ceil.toString()); + } - FunctionExpression ceiling = dsl.ceiling(DSL.literal(value)); + /** + * Test ceil with double value. + */ + @ParameterizedTest(name = "ceil({0})") + @ValueSource(doubles = {-2L, 2L}) + public void ceil_double_value(Double value) { + FunctionExpression ceil = dsl.ceil(DSL.literal(value)); assertThat( - ceiling.valueOf(valueEnv()), - allOf(hasType(LONG), hasValue((long) Math.ceil(value)))); - assertEquals(String.format("ceiling(%s)", value.toString()), ceiling.toString()); + ceil.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue(Math.ceil(value)))); + assertEquals(String.format("ceil(%s)", value.toString()), ceil.toString()); } @Test @@ -143,10 +185,6 @@ public void ceil_null_value() { FunctionExpression ceil = dsl.ceil(DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD, DOUBLE)); assertEquals(LONG, ceil.type()); assertTrue(ceil.valueOf(valueEnv()).isNull()); - - FunctionExpression ceiling = dsl.ceiling(DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD, DOUBLE)); - assertEquals(LONG, ceiling.type()); - assertTrue(ceiling.valueOf(valueEnv()).isNull()); } @Test @@ -154,17 +192,52 @@ public void ceil_missing_value() { FunctionExpression ceil = dsl.ceil(DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD, DOUBLE)); assertEquals(LONG, ceil.type()); assertTrue(ceil.valueOf(valueEnv()).isMissing()); + } - ceil = dsl.ceiling(DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD, DOUBLE)); - assertEquals(LONG, ceil.type()); - assertTrue(ceil.valueOf(valueEnv()).isMissing()); + /** + * Test exp with integer value. + */ + @ParameterizedTest(name = "exp({0})") + @ValueSource(ints = {-2, 2}) + public void exp_int_value(Integer value) { + FunctionExpression exp = dsl.exp(DSL.literal(value)); + assertThat( + exp.valueOf(valueEnv()), + allOf(hasType(DOUBLE), hasValue(Math.exp(value)))); + assertEquals(String.format("exp(%s)", value.toString()), exp.toString()); + } + + /** + * Test exp with long value. + */ + @ParameterizedTest(name = "exp({0})") + @ValueSource(longs = {-2L, 2L}) + public void exp_long_value(Long value) { + FunctionExpression exp = dsl.exp(DSL.literal(value)); + assertThat( + exp.valueOf(valueEnv()), + allOf(hasType(DOUBLE), hasValue(Math.exp(value)))); + assertEquals(String.format("exp(%s)", value.toString()), exp.toString()); + } + + /** + * Test exp with float value. + */ + @ParameterizedTest(name = "exp({0})") + @ValueSource(floats = {-2F, 2F}) + public void exp_float_value(Float value) { + FunctionExpression exp = dsl.exp(DSL.literal(value)); + assertThat( + exp.valueOf(valueEnv()), + allOf(hasType(DOUBLE), hasValue(Math.exp(value)))); + assertEquals(String.format("exp(%s)", value.toString()), exp.toString()); } /** * Test exp with double value. */ @ParameterizedTest(name = "exp({0})") - @ValueSource(doubles = {-2L, 2L}) + @ValueSource(doubles = {-2D, 2D}) public void exp_double_value(Double value) { FunctionExpression exp = dsl.exp(DSL.literal(value)); assertThat( @@ -187,6 +260,45 @@ public void exp_missing_value() { assertTrue(exp.valueOf(valueEnv()).isMissing()); } + /** + * Test floor with integer value. + */ + @ParameterizedTest(name = "floor({0})") + @ValueSource(ints = {-2, 2}) + public void floor_int_value(Integer value) { + FunctionExpression floor = dsl.floor(DSL.literal(value)); + assertThat( + floor.valueOf(valueEnv()), + allOf(hasType(INTEGER), hasValue(((int) Math.floor(value))))); + assertEquals(String.format("floor(%s)", value.toString()), floor.toString()); + } + + /** + * Test floor with long value. + */ + @ParameterizedTest(name = "floor({0})") + @ValueSource(longs = {-2L, 2L}) + public void floor_long_value(Long value) { + FunctionExpression floor = dsl.floor(DSL.literal(value)); + assertThat( + floor.valueOf(valueEnv()), + allOf(hasType(LONG), hasValue(((long) Math.floor(value))))); + assertEquals(String.format("floor(%s)", value.toString()), floor.toString()); + } + + /** + * Test floor with float value. + */ + @ParameterizedTest(name = "floor({0})") + @ValueSource(floats = {-2F, 2F}) + public void floor_float_value(Float value) { + FunctionExpression floor = dsl.floor(DSL.literal(value)); + assertThat( + floor.valueOf(valueEnv()), + allOf(hasType(FLOAT), hasValue(((float) Math.floor(value))))); + assertEquals(String.format("floor(%s)", value.toString()), floor.toString()); + } + /** * Test floor with double value. */ @@ -196,7 +308,7 @@ public void floor_double_value(Double value) { FunctionExpression floor = dsl.floor(DSL.literal(value)); assertThat( floor.valueOf(valueEnv()), - allOf(hasType(LONG), hasValue(((long) Math.floor(value))))); + allOf(hasType(DOUBLE), hasValue((Math.floor(value))))); assertEquals(String.format("floor(%s)", value.toString()), floor.toString()); } @@ -214,11 +326,50 @@ public void floor_missing_value() { assertTrue(floor.valueOf(valueEnv()).isMissing()); } + /** + * Test ln with integer value. + */ + @ParameterizedTest(name = "ln({0})") + @ValueSource(ints = {2, -2}) + public void ln_int_value(Integer value) { + FunctionExpression ln = dsl.ln(DSL.literal(value)); + assertThat( + ln.valueOf(valueEnv()), + allOf(hasType(DOUBLE), hasValue(Math.log(value)))); + assertEquals(String.format("ln(%s)", value.toString()), ln.toString()); + } + + /** + * Test ln with long value. + */ + @ParameterizedTest(name = "ln({0})") + @ValueSource(longs = {2L, -2L}) + public void ln_long_value(Long value) { + FunctionExpression ln = dsl.ln(DSL.literal(value)); + assertThat( + ln.valueOf(valueEnv()), + allOf(hasType(DOUBLE), hasValue(Math.log(value)))); + assertEquals(String.format("ln(%s)", value.toString()), ln.toString()); + } + + /** + * Test ln with float value. + */ + @ParameterizedTest(name = "ln({0})") + @ValueSource(floats = {2F, -2F}) + public void ln_float_value(Float value) { + FunctionExpression ln = dsl.ln(DSL.literal(value)); + assertThat( + ln.valueOf(valueEnv()), + allOf(hasType(DOUBLE), hasValue(Math.log(value)))); + assertEquals(String.format("ln(%s)", value.toString()), ln.toString()); + } + /** * Test ln with double value. */ @ParameterizedTest(name = "ln({0})") - @ValueSource(doubles = {2D}) + @ValueSource(doubles = {2D, -2D}) public void ln_double_value(Double value) { FunctionExpression ln = dsl.ln(DSL.literal(value)); assertThat( @@ -241,16 +392,59 @@ public void ln_missing_value() { assertTrue(ln.valueOf(valueEnv()).isMissing()); } + /** + * Test log with int value. + */ + @ParameterizedTest(name = "log({0}, {1})") + @MethodSource("testLogIntegerArguments") + public void log_int_value(Integer v1, Integer v2) { + FunctionExpression log = dsl.log(DSL.literal(v1), DSL.literal(v2)); + assertEquals(log.type(), DOUBLE); + assertThat( + getDoubleValue(log.valueOf(valueEnv())), + closeTo(Math.log(v2) / Math.log(v1), 0.0001)); + assertEquals(String.format("log(%s, %s)", v1.toString(), v2.toString()), log.toString()); + } + + /** + * Test log with long value. + */ + @ParameterizedTest(name = "log({0}, {1})") + @MethodSource("testLogLongArguments") + public void log_long_value(Long v1, Long v2) { + FunctionExpression log = dsl.log(DSL.literal(v1), DSL.literal(v2)); + assertEquals(log.type(), DOUBLE); + assertThat( + getDoubleValue(log.valueOf(valueEnv())), + closeTo(Math.log(v2) / Math.log(v1), 0.0001)); + assertEquals(String.format("log(%s, %s)", v1.toString(), v2.toString()), log.toString()); + } + + /** + * Test log with float value. + */ + @ParameterizedTest(name = "log({0}, {1})") + @MethodSource("testLogFloatArguments") + public void log_double_value(Float v1, Float v2) { + FunctionExpression log = dsl.log(DSL.literal(v1), DSL.literal(v2)); + assertEquals(log.type(), DOUBLE); + assertThat( + getDoubleValue(log.valueOf(valueEnv())), + closeTo(Math.log(v2) / Math.log(v1), 0.0001)); + assertEquals(String.format("log(%s, %s)", v1.toString(), v2.toString()), log.toString()); + } + /** * Test log with double value. */ @ParameterizedTest(name = "log({0}, {1})") - @MethodSource("testLogArguments") + @MethodSource("testLogDoubleArguments") public void log_double_value(Double v1, Double v2) { FunctionExpression log = dsl.log(DSL.literal(v1), DSL.literal(v2)); + assertEquals(log.type(), DOUBLE); assertThat( - log.valueOf(valueEnv()), - allOf(hasType(DOUBLE), hasValue(Math.log(v2) / Math.log(v1)))); + getDoubleValue(log.valueOf(valueEnv())), + closeTo(Math.log(v2) / Math.log(v1), 0.0001)); assertEquals(String.format("log(%s, %s)", v1.toString(), v2.toString()), log.toString()); } diff --git a/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/ppl/MathematicalFunctionIT.java b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/ppl/MathematicalFunctionIT.java index 443dd520c3..bc44ed144a 100644 --- a/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/ppl/MathematicalFunctionIT.java +++ b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/ppl/MathematicalFunctionIT.java @@ -51,7 +51,7 @@ public void testCeil() throws IOException { executeQuery( String.format( "source=%s | eval f = ceil(age) | fields f", TEST_INDEX_BANK)); - verifySchema(result, schema("f", null, "long")); + verifySchema(result, schema("f", null, "integer")); verifyDataRows( result, rows(32), rows(36), rows(28), rows(33), rows(36), rows(39), rows(34)); @@ -75,7 +75,7 @@ public void testFloor() throws IOException { executeQuery( String.format( "source=%s | eval f = floor(age) | fields f", TEST_INDEX_BANK)); - verifySchema(result, schema("f", null, "long")); + verifySchema(result, schema("f", null, "integer")); verifyDataRows( result, rows(32), rows(36), rows(28), rows(33), rows(36), rows(39), rows(34)); diff --git a/ppl/src/main/antlr/OpenDistroPPLLexer.g4 b/ppl/src/main/antlr/OpenDistroPPLLexer.g4 index 494aa1d911..3cca57bfa4 100644 --- a/ppl/src/main/antlr/OpenDistroPPLLexer.g4 +++ b/ppl/src/main/antlr/OpenDistroPPLLexer.g4 @@ -136,7 +136,6 @@ DC: 'DC'; // BASIC FUNCTIONS ABS: 'ABS'; CEIL: 'CEIL'; -CEILING: 'CEILING'; EXP: 'EXP'; FLOOR: 'FLOOR'; LN: 'LN'; diff --git a/ppl/src/main/antlr/OpenDistroPPLParser.g4 b/ppl/src/main/antlr/OpenDistroPPLParser.g4 index d1a5125b28..6cfc5d6987 100644 --- a/ppl/src/main/antlr/OpenDistroPPLParser.g4 +++ b/ppl/src/main/antlr/OpenDistroPPLParser.g4 @@ -203,7 +203,7 @@ functionArg ; mathematicalFunctionBase - : ABS | CEIL | CEILING | EXP | FLOOR | LN | LOG + : ABS | CEIL | EXP | FLOOR | LN | LOG ; dateAndTimeFunctionBase From a0c3a53a786e15dcff1a0e499b78810742eeef2d Mon Sep 17 00:00:00 2001 From: chloe-zh Date: Wed, 8 Jul 2020 21:41:52 -0700 Subject: [PATCH 05/11] Function signature changed consistent with mysql --- .../operator/arthmetic/MathematicalFunctionTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 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 413fb8c5ad..1f24f0bf4a 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 @@ -183,14 +183,14 @@ public void ceil_double_value(Double value) { @Test public void ceil_null_value() { FunctionExpression ceil = dsl.ceil(DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD, DOUBLE)); - assertEquals(LONG, ceil.type()); + assertEquals(DOUBLE, ceil.type()); assertTrue(ceil.valueOf(valueEnv()).isNull()); } @Test public void ceil_missing_value() { FunctionExpression ceil = dsl.ceil(DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD, DOUBLE)); - assertEquals(LONG, ceil.type()); + assertEquals(DOUBLE, ceil.type()); assertTrue(ceil.valueOf(valueEnv()).isMissing()); } @@ -315,14 +315,14 @@ public void floor_double_value(Double value) { @Test public void floor_null_value() { FunctionExpression floor = dsl.floor(DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD, DOUBLE)); - assertEquals(LONG, floor.type()); + assertEquals(DOUBLE, floor.type()); assertTrue(floor.valueOf(valueEnv()).isNull()); } @Test public void floor_missing_value() { FunctionExpression floor = dsl.floor(DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD, DOUBLE)); - assertEquals(LONG, floor.type()); + assertEquals(DOUBLE, floor.type()); assertTrue(floor.valueOf(valueEnv()).isMissing()); } From ff1d3a9e72713cd104edb448da927c4cc8b20bb5 Mon Sep 17 00:00:00 2001 From: chloe-zh Date: Fri, 10 Jul 2020 10:07:25 -0700 Subject: [PATCH 06/11] Function signature changed consistent with mysql --- .../sql/expression/DSL.java | 15 + .../function/BuiltinFunctionName.java | 3 + .../arthmetic/MathematicalFunction.java | 174 +++++---- .../arthmetic/MathematicalFunctionTest.java | 363 ++++++++++++++++-- .../sql/ppl/MathematicalFunctionIT.java | 56 ++- .../sql/util/MatcherUtils.java | 10 + ppl/src/main/antlr/OpenDistroPPLLexer.g4 | 3 + ppl/src/main/antlr/OpenDistroPPLParser.g4 | 2 +- sql/src/main/antlr/OpenDistroSQLLexer.g4 | 1 + sql/src/main/antlr/OpenDistroSQLParser.g4 | 2 +- 10 files changed, 518 insertions(+), 111 deletions(-) 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 f13fab346b..0660a8496c 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 @@ -71,6 +71,11 @@ public FunctionExpression ceil(Expression... expressions) { repository.compile(BuiltinFunctionName.CEIL.getName(), Arrays.asList(expressions)); } + public FunctionExpression ceiling(Expression... expressions) { + return (FunctionExpression) + repository.compile(BuiltinFunctionName.CEILING.getName(), Arrays.asList(expressions)); + } + public FunctionExpression exp(Expression... expressions) { return (FunctionExpression) repository.compile(BuiltinFunctionName.EXP.getName(), Arrays.asList(expressions)); @@ -91,6 +96,16 @@ public FunctionExpression log(Expression... expressions) { repository.compile(BuiltinFunctionName.LOG.getName(), Arrays.asList(expressions)); } + public FunctionExpression log10(Expression... expressions) { + return (FunctionExpression) + repository.compile(BuiltinFunctionName.LOG10.getName(), Arrays.asList(expressions)); + } + + public FunctionExpression log2(Expression... expressions) { + return (FunctionExpression) + repository.compile(BuiltinFunctionName.LOG2.getName(), Arrays.asList(expressions)); + } + public FunctionExpression add(Expression... expressions) { return (FunctionExpression) repository.compile(BuiltinFunctionName.ADD.getName(), Arrays.asList(expressions)); diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/BuiltinFunctionName.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/BuiltinFunctionName.java index 2d2c6d0898..d3fc9c66a0 100644 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/BuiltinFunctionName.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/function/BuiltinFunctionName.java @@ -17,10 +17,13 @@ public enum BuiltinFunctionName { */ ABS(FunctionName.of("abs")), CEIL(FunctionName.of("ceil")), + CEILING(FunctionName.of("ceiling")), EXP(FunctionName.of("exp")), FLOOR(FunctionName.of("floor")), LN(FunctionName.of("ln")), LOG(FunctionName.of("log")), + LOG10(FunctionName.of("log10")), + LOG2(FunctionName.of("log2")), /** * Text Functions. 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 3f657cda88..962ddf15eb 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 @@ -43,19 +43,18 @@ public class MathematicalFunction { public static void register(BuiltinFunctionRepository repository) { repository.register(abs()); repository.register(ceil()); + repository.register(ceiling()); repository.register(exp()); repository.register(floor()); repository.register(ln()); repository.register(log()); + repository.register(log10()); + repository.register(log2()); } /** - * Definition of abs() function. - * The supported signature of abs() function are - * INT -> INT - * LONG -> LONG - * FLOAT -> FLOAT - * DOUBLE -> DOUBLE + * Definition of abs() function. The supported signature of abs() function are INT -> INT LONG -> + * LONG FLOAT -> FLOAT DOUBLE -> DOUBLE */ private static FunctionResolver abs() { return new FunctionResolver( @@ -65,89 +64,127 @@ private static FunctionResolver abs() { } /** - * Definition of ceil(x) function. - * Calculate the next highest integer that x rounds up to - * The supported signature of ceil/ceiling function is - * INTEGER -> INTEGER - * LONG -> LONG - * FLOAT -> FLOAT - * DOUBLE -> DOUBLE + * Definition of ceil(x)/ceiling(x) function. Calculate the next highest integer that x rounds up + * to The supported signature of ceil/ceiling function is DOUBLE -> INTEGER */ private static FunctionResolver ceil() { FunctionName functionName = BuiltinFunctionName.CEIL.getName(); return new FunctionResolver( functionName, - singleArgumentFunction( - functionName, - v -> (int) Math.ceil(v), - v -> (long) Math.ceil(v), - v -> (float) Math.ceil(v), - Math::ceil - )); + new ImmutableMap.Builder() + .put( + new FunctionSignature(functionName, Arrays.asList(ExprCoreType.DOUBLE)), + unaryOperator( + functionName, + v -> ((int) Math.ceil(v)), + ExprValueUtils::getDoubleValue, + ExprCoreType.INTEGER)) + .build()); + } + + 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()); } /** - * Definition of exp(x) function. - * Calculate exponent function e to the x - * The supported signature of exp function is - * INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE + * Definition of exp(x) function. Calculate exponent function e to the x The supported signature + * of exp function is INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE */ private static FunctionResolver exp() { - return new FunctionResolver(BuiltinFunctionName.EXP.getName(), + return new FunctionResolver( + BuiltinFunctionName.EXP.getName(), singleArgumentFunction(BuiltinFunctionName.EXP.getName(), Math::exp)); } /** - * Definition of floor(x) function. - * Calculate the next nearest whole integer that x rounds down to - * The supported signature of floor function is - * INTEGER -> INTEGER - * LONG -> LONG - * FLOAT -> FLOAT - * DOUBLE -> DOUBLE + * Definition of floor(x) function. Calculate the next nearest whole integer that x rounds down to + * The supported signature of floor function is DOUBLE -> INTEGER */ private static FunctionResolver floor() { FunctionName functionName = BuiltinFunctionName.FLOOR.getName(); return new FunctionResolver( functionName, - singleArgumentFunction( - functionName, - v -> (int) Math.floor(v), - v -> (long) Math.floor(v), - v -> (float) Math.floor(v), - Math::floor - )); + new ImmutableMap.Builder() + .put( + new FunctionSignature(functionName, Arrays.asList(ExprCoreType.DOUBLE)), + unaryOperator( + functionName, + v -> ((int) Math.floor(v)), + ExprValueUtils::getDoubleValue, + ExprCoreType.INTEGER)) + .build()); } /** - * Definition of ln(x) function. - * Calculate the natural logarithm of x - * The supported signature of ln function is - * INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE + * Definition of ln(x) function. Calculate the natural logarithm of x The supported signature of + * ln function is INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE */ private static FunctionResolver ln() { - return new FunctionResolver(BuiltinFunctionName.LN.getName(), + return new FunctionResolver( + BuiltinFunctionName.LN.getName(), singleArgumentFunction(BuiltinFunctionName.LN.getName(), Math::log)); } /** - * Definition of log(x, y) function. - * Calculate the logarithm of x using y as the base - * The supported signature of log function is - * (x: INTEGER/LONG/FLOAT/DOUBLE, y: INTEGER/LONG/FLOAT/DOUBLE) -> DOUBLE + * Definition of log(b, x) function. Calculate the logarithm of x using b as the base The + * supported signature of log function is (b: INTEGER/LONG/FLOAT/DOUBLE, x: + * INTEGER/LONG/FLOAT/DOUBLE]) -> DOUBLE */ private static FunctionResolver log() { - return new FunctionResolver(BuiltinFunctionName.LOG.getName(), - doubleArgumentsFunction( - BuiltinFunctionName.LOG.getName(), (v1, v2) -> Math.log(v2) / Math.log(v1))); + 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()); + } + + /** + * Definition of log10(x) function. Calculate base-10 logarithm of x The supported signature of + * log function is INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE + */ + private static FunctionResolver log10() { + return new FunctionResolver( + BuiltinFunctionName.LOG10.getName(), + singleArgumentFunction(BuiltinFunctionName.LOG10.getName(), Math::log10)); } /** - * Util method to generate single argument function bundles. Applicable for - * INTEGER -> INTEGER - * LONG -> LONG - * FLOAT -> FLOAT - * DOUBLE -> DOUBLE + * Definition of log2(x) function. Calculate base-2 logarithm of x The supported signature of log + * function is 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))); + } + + /** + * Util method to generate single argument function bundles. Applicable for INTEGER -> INTEGER + * LONG -> LONG FLOAT -> FLOAT DOUBLE -> DOUBLE */ private static Map singleArgumentFunction( FunctionName functionName, @@ -173,13 +210,9 @@ private static Map singleArgumentFunction( return builder.build(); } - /** - * Util method to generate single argument function bundles. Applicable for - * DOUBLE (INTEGER/LONG/FLOAT) -> DOUBLE - */ + /** Util method to generate single argument function bundles. Applicable for DOUBLE -> DOUBLE */ private static Map singleArgumentFunction( - FunctionName functionName, - Function doubleFunc) { + FunctionName functionName, Function doubleFunc) { ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); return builder .put( @@ -188,21 +221,4 @@ private static Map singleArgumentFunction( functionName, doubleFunc, ExprValueUtils::getDoubleValue, ExprCoreType.DOUBLE)) .build(); } - - /** - * Util method to generate single argument function bundles. Applicable for - * (DOUBLE, DOUBLE) -> DOUBLE - */ - private static Map doubleArgumentsFunction( - FunctionName functionName, - BiFunction doubleFunc) { - ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); - return builder - .put( - new FunctionSignature( - functionName, Arrays.asList(ExprCoreType.DOUBLE, ExprCoreType.DOUBLE)), - doubleArgFunc(functionName, doubleFunc, ExprValueUtils::getDoubleValue, - ExprValueUtils::getDoubleValue, ExprCoreType.DOUBLE)) - .build(); - } } 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 1f24f0bf4a..57e6fa5c7b 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 @@ -35,8 +35,6 @@ import com.amazon.opendistroforelasticsearch.sql.expression.DSL; import com.amazon.opendistroforelasticsearch.sql.expression.ExpressionTestBase; import com.amazon.opendistroforelasticsearch.sql.expression.FunctionExpression; -import java.util.Arrays; -import java.util.List; import java.util.stream.Stream; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; @@ -131,67 +129,101 @@ public void abs_missing_value() { dsl.abs(DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER)).valueOf(valueEnv()).isMissing()); } - /** - * Test ceil with integer value. + * Test ceil/ceiling with integer value. */ @ParameterizedTest(name = "ceil({0})") @ValueSource(ints = {2, -2}) public void ceil_int_value(Integer value) { FunctionExpression ceil = dsl.ceil(DSL.literal(value)); assertThat( - ceil.valueOf(valueEnv()), allOf(hasType(INTEGER), hasValue((int) Math.ceil(value)))); + ceil.valueOf(valueEnv()), + allOf(hasType(INTEGER), hasValue((int) Math.ceil(value)))); assertEquals(String.format("ceil(%s)", value.toString()), ceil.toString()); + + FunctionExpression ceiling = dsl.ceiling(DSL.literal(value)); + assertThat( + ceiling.valueOf(valueEnv()), allOf(hasType(INTEGER), hasValue((int) Math.ceil(value)))); + assertEquals(String.format("ceiling(%s)", value.toString()), ceiling.toString()); } /** - * Test ceil with long value. + * Test ceil/ceiling with long value. */ @ParameterizedTest(name = "ceil({0})") @ValueSource(longs = {2L, -2L}) public void ceil_long_value(Long value) { FunctionExpression ceil = dsl.ceil(DSL.literal(value)); assertThat( - ceil.valueOf(valueEnv()), allOf(hasType(LONG), hasValue((long) Math.ceil(value)))); + ceil.valueOf(valueEnv()), allOf(hasType(INTEGER), hasValue((int) Math.ceil(value)))); assertEquals(String.format("ceil(%s)", value.toString()), ceil.toString()); + + FunctionExpression ceiling = dsl.ceiling(DSL.literal(value)); + assertThat( + ceiling.valueOf(valueEnv()), allOf(hasType(INTEGER), hasValue((int) Math.ceil(value)))); + assertEquals(String.format("ceiling(%s)", value.toString()), ceiling.toString()); } /** - * Test ceil with float value. + * Test ceil/ceiling with float value. */ @ParameterizedTest(name = "ceil({0})") @ValueSource(floats = {2F, -2F}) public void ceil_float_value(Float value) { FunctionExpression ceil = dsl.ceil(DSL.literal(value)); assertThat( - ceil.valueOf(valueEnv()), allOf(hasType(FLOAT), hasValue((float) Math.ceil(value)))); + ceil.valueOf(valueEnv()), allOf(hasType(INTEGER), hasValue((int) Math.ceil(value)))); assertEquals(String.format("ceil(%s)", value.toString()), ceil.toString()); + + FunctionExpression ceiling = dsl.ceiling(DSL.literal(value)); + assertThat( + ceiling.valueOf(valueEnv()), allOf(hasType(INTEGER), hasValue((int) Math.ceil(value)))); + assertEquals(String.format("ceiling(%s)", value.toString()), ceiling.toString()); } /** - * Test ceil with double value. + * Test ceil/ceiling with double value. */ @ParameterizedTest(name = "ceil({0})") @ValueSource(doubles = {-2L, 2L}) public void ceil_double_value(Double value) { FunctionExpression ceil = dsl.ceil(DSL.literal(value)); assertThat( - ceil.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue(Math.ceil(value)))); + ceil.valueOf(valueEnv()), allOf(hasType(INTEGER), hasValue((int) Math.ceil(value)))); assertEquals(String.format("ceil(%s)", value.toString()), ceil.toString()); + + FunctionExpression ceiling = dsl.ceiling(DSL.literal(value)); + assertThat( + ceiling.valueOf(valueEnv()), allOf(hasType(INTEGER), hasValue((int) Math.ceil(value)))); + assertEquals(String.format("ceiling(%s)", value.toString()), ceiling.toString()); } + /** + * Test ceil/ceiling with null value. + */ @Test public void ceil_null_value() { FunctionExpression ceil = dsl.ceil(DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD, DOUBLE)); - assertEquals(DOUBLE, ceil.type()); + assertEquals(INTEGER, ceil.type()); assertTrue(ceil.valueOf(valueEnv()).isNull()); + + FunctionExpression ceiling = dsl.ceiling(DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD, DOUBLE)); + assertEquals(INTEGER, ceiling.type()); + assertTrue(ceiling.valueOf(valueEnv()).isNull()); } + /** + * Test ceil/ceiling with missing value. + */ @Test public void ceil_missing_value() { FunctionExpression ceil = dsl.ceil(DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD, DOUBLE)); - assertEquals(DOUBLE, ceil.type()); + assertEquals(INTEGER, ceil.type()); assertTrue(ceil.valueOf(valueEnv()).isMissing()); + + FunctionExpression ceiling = dsl.ceiling(DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD, DOUBLE)); + assertEquals(INTEGER, ceiling.type()); + assertTrue(ceiling.valueOf(valueEnv()).isMissing()); } /** @@ -246,6 +278,9 @@ public void exp_double_value(Double value) { assertEquals(String.format("exp(%s)", value.toString()), exp.toString()); } + /** + * Test exp with null value. + */ @Test public void exp_null_value() { FunctionExpression exp = dsl.exp(DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD, DOUBLE)); @@ -253,6 +288,9 @@ public void exp_null_value() { assertTrue(exp.valueOf(valueEnv()).isNull()); } + /** + * Test exp with missing value. + */ @Test public void exp_missing_value() { FunctionExpression exp = dsl.exp(DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD, DOUBLE)); @@ -269,7 +307,7 @@ public void floor_int_value(Integer value) { FunctionExpression floor = dsl.floor(DSL.literal(value)); assertThat( floor.valueOf(valueEnv()), - allOf(hasType(INTEGER), hasValue(((int) Math.floor(value))))); + allOf(hasType(INTEGER), hasValue((int) Math.floor(value)))); assertEquals(String.format("floor(%s)", value.toString()), floor.toString()); } @@ -282,7 +320,7 @@ public void floor_long_value(Long value) { FunctionExpression floor = dsl.floor(DSL.literal(value)); assertThat( floor.valueOf(valueEnv()), - allOf(hasType(LONG), hasValue(((long) Math.floor(value))))); + allOf(hasType(INTEGER), hasValue((int) Math.floor(value)))); assertEquals(String.format("floor(%s)", value.toString()), floor.toString()); } @@ -295,7 +333,7 @@ public void floor_float_value(Float value) { FunctionExpression floor = dsl.floor(DSL.literal(value)); assertThat( floor.valueOf(valueEnv()), - allOf(hasType(FLOAT), hasValue(((float) Math.floor(value))))); + allOf(hasType(INTEGER), hasValue((int) Math.floor(value)))); assertEquals(String.format("floor(%s)", value.toString()), floor.toString()); } @@ -308,21 +346,27 @@ public void floor_double_value(Double value) { FunctionExpression floor = dsl.floor(DSL.literal(value)); assertThat( floor.valueOf(valueEnv()), - allOf(hasType(DOUBLE), hasValue((Math.floor(value))))); + allOf(hasType(INTEGER), hasValue((int) Math.floor(value)))); assertEquals(String.format("floor(%s)", value.toString()), floor.toString()); } + /** + * Test floor with null value. + */ @Test public void floor_null_value() { FunctionExpression floor = dsl.floor(DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD, DOUBLE)); - assertEquals(DOUBLE, floor.type()); + assertEquals(INTEGER, floor.type()); assertTrue(floor.valueOf(valueEnv()).isNull()); } + /** + * Test floor with missing value. + */ @Test public void floor_missing_value() { FunctionExpression floor = dsl.floor(DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD, DOUBLE)); - assertEquals(DOUBLE, floor.type()); + assertEquals(INTEGER, floor.type()); assertTrue(floor.valueOf(valueEnv()).isMissing()); } @@ -378,6 +422,9 @@ public void ln_double_value(Double value) { assertEquals(String.format("ln(%s)", value.toString()), ln.toString()); } + /** + * Test ln with null value. + */ @Test public void ln_null_value() { FunctionExpression ln = dsl.ln(DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD, DOUBLE)); @@ -385,6 +432,9 @@ public void ln_null_value() { assertTrue(ln.valueOf(valueEnv()).isNull()); } + /** + * Test ln with missing value. + */ @Test public void ln_missing_value() { FunctionExpression ln = dsl.ln(DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD, DOUBLE)); @@ -393,11 +443,93 @@ public void ln_missing_value() { } /** - * Test log with int value. + * Test log with 1 int argument. + */ + @ParameterizedTest(name = "log({0})") + @ValueSource(ints = {2, 3}) + public void log_int_value(Integer v) { + FunctionExpression log = dsl.log(DSL.literal(v)); + assertEquals(log.type(), DOUBLE); + assertThat( + getDoubleValue(log.valueOf(valueEnv())), + closeTo(Math.log(v), 0.0001) + ); + assertEquals(String.format("log(%s)", v.toString()), log.toString()); + } + + /** + * Test log with 1 long argument. + */ + @ParameterizedTest(name = "log({0})") + @ValueSource(longs = {2L, 3L}) + public void log_int_value(Long v) { + FunctionExpression log = dsl.log(DSL.literal(v)); + assertEquals(log.type(), DOUBLE); + assertThat( + getDoubleValue(log.valueOf(valueEnv())), + closeTo(Math.log(v), 0.0001) + ); + assertEquals(String.format("log(%s)", v.toString()), log.toString()); + } + + /** + * Test log with 1 float argument. + */ + @ParameterizedTest(name = "log({0})") + @ValueSource(floats = {2F, 3F}) + public void log_float_value(Float v) { + FunctionExpression log = dsl.log(DSL.literal(v)); + assertEquals(log.type(), DOUBLE); + assertThat( + getDoubleValue(log.valueOf(valueEnv())), + closeTo(Math.log(v), 0.0001) + ); + assertEquals(String.format("log(%s)", v.toString()), log.toString()); + } + + /** + * Test log with 1 double argument. + */ + @ParameterizedTest(name = "log({0})") + @ValueSource(doubles = {2D, 3D}) + public void log_double_value(Double v) { + FunctionExpression log = dsl.log(DSL.literal(v)); + assertEquals(log.type(), DOUBLE); + assertThat( + getDoubleValue(log.valueOf(valueEnv())), + closeTo(Math.log(v), 0.0001) + ); + assertEquals(String.format("log(%s)", v.toString()), log.toString()); + } + + /** + * Test log with 1 null value argument. + */ + @Test + public void log_null_value() { + FunctionExpression log = dsl.log( + DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD, DOUBLE)); + assertEquals(DOUBLE, log.type()); + assertTrue(log.valueOf(valueEnv()).isNull()); + } + + /** + * Test log with 1 missing value argument. + */ + @Test + public void log_missing_value() { + FunctionExpression log = dsl.log( + DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD, DOUBLE)); + assertEquals(DOUBLE, log.type()); + assertTrue(log.valueOf(valueEnv()).isMissing()); + } + + /** + * Test log with 2 int arguments. */ @ParameterizedTest(name = "log({0}, {1})") @MethodSource("testLogIntegerArguments") - public void log_int_value(Integer v1, Integer v2) { + public void log_two_int_value(Integer v1, Integer v2) { FunctionExpression log = dsl.log(DSL.literal(v1), DSL.literal(v2)); assertEquals(log.type(), DOUBLE); assertThat( @@ -407,11 +539,11 @@ public void log_int_value(Integer v1, Integer v2) { } /** - * Test log with long value. + * Test log with 2 long arguments. */ @ParameterizedTest(name = "log({0}, {1})") @MethodSource("testLogLongArguments") - public void log_long_value(Long v1, Long v2) { + public void log_two_long_value(Long v1, Long v2) { FunctionExpression log = dsl.log(DSL.literal(v1), DSL.literal(v2)); assertEquals(log.type(), DOUBLE); assertThat( @@ -421,11 +553,11 @@ public void log_long_value(Long v1, Long v2) { } /** - * Test log with float value. + * Test log with 2 float arguments. */ @ParameterizedTest(name = "log({0}, {1})") @MethodSource("testLogFloatArguments") - public void log_double_value(Float v1, Float v2) { + public void log_two_double_value(Float v1, Float v2) { FunctionExpression log = dsl.log(DSL.literal(v1), DSL.literal(v2)); assertEquals(log.type(), DOUBLE); assertThat( @@ -435,11 +567,11 @@ public void log_double_value(Float v1, Float v2) { } /** - * Test log with double value. + * Test log with 2 double arguments. */ @ParameterizedTest(name = "log({0}, {1})") @MethodSource("testLogDoubleArguments") - public void log_double_value(Double v1, Double v2) { + public void log_two_double_value(Double v1, Double v2) { FunctionExpression log = dsl.log(DSL.literal(v1), DSL.literal(v2)); assertEquals(log.type(), DOUBLE); assertThat( @@ -448,8 +580,11 @@ public void log_double_value(Double v1, Double v2) { assertEquals(String.format("log(%s, %s)", v1.toString(), v2.toString()), log.toString()); } + /** + * Test log with 2 null value arguments. + */ @Test - public void log_null_value() { + public void log_two_null_value() { FunctionExpression log = dsl.log( DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD, DOUBLE), DSL.literal(2D)); assertEquals(DOUBLE, log.type()); @@ -466,8 +601,11 @@ public void log_null_value() { assertTrue(log.valueOf(valueEnv()).isNull()); } + /** + * Test log with 2 missing value arguments. + */ @Test - public void log_missing_value() { + public void log_two_missing_value() { FunctionExpression log = dsl.log( DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD, DOUBLE), DSL.literal(2D)); assertEquals(DOUBLE, log.type()); @@ -484,6 +622,9 @@ public void log_missing_value() { assertTrue(log.valueOf(valueEnv()).isMissing()); } + /** + * Test log with null and missing value arguments. + */ @Test public void log_null_missing() { FunctionExpression log = dsl.log( @@ -498,4 +639,168 @@ public void log_null_missing() { assertEquals(DOUBLE, log.type()); assertTrue(log.valueOf(valueEnv()).isMissing()); } + + /** + * Test log10 with int value. + */ + @ParameterizedTest(name = "log10({0})") + @ValueSource(ints = {2, 3}) + public void log10_int_value(Integer v) { + FunctionExpression log = dsl.log10(DSL.literal(v)); + assertEquals(log.type(), DOUBLE); + assertThat( + getDoubleValue(log.valueOf(valueEnv())), + closeTo(Math.log10(v), 0.0001) + ); + assertEquals(String.format("log10(%s)", v.toString()), log.toString()); + } + + /** + * Test log10 with long value. + */ + @ParameterizedTest(name = "log10({0})") + @ValueSource(longs = {2L, 3L}) + public void log10_long_value(Long v) { + FunctionExpression log = dsl.log10(DSL.literal(v)); + assertEquals(log.type(), DOUBLE); + assertThat( + getDoubleValue(log.valueOf(valueEnv())), + closeTo(Math.log10(v), 0.0001) + ); + assertEquals(String.format("log10(%s)", v.toString()), log.toString()); + } + + /** + * Test log10 with float value. + */ + @ParameterizedTest(name = "log10({0})") + @ValueSource(floats = {2F, 3F}) + public void log10_float_value(Float v) { + FunctionExpression log = dsl.log10(DSL.literal(v)); + assertEquals(log.type(), DOUBLE); + assertThat( + getDoubleValue(log.valueOf(valueEnv())), + closeTo(Math.log10(v), 0.0001) + ); + assertEquals(String.format("log10(%s)", v.toString()), log.toString()); + } + + /** + * Test log10 with int value. + */ + @ParameterizedTest(name = "log10({0})") + @ValueSource(doubles = {2D, 3D}) + public void log10_double_value(Double v) { + FunctionExpression log = dsl.log10(DSL.literal(v)); + assertEquals(log.type(), DOUBLE); + assertThat( + getDoubleValue(log.valueOf(valueEnv())), + closeTo(Math.log10(v), 0.0001) + ); + assertEquals(String.format("log10(%s)", v.toString()), log.toString()); + } + + /** + * Test log10 with null value. + */ + @Test + public void log10_null_value() { + FunctionExpression log = dsl.log10( + DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD, DOUBLE)); + assertEquals(DOUBLE, log.type()); + assertTrue(log.valueOf(valueEnv()).isNull()); + } + + /** + * Test log10 with missing value. + */ + @Test + public void log10_missing_value() { + FunctionExpression log = dsl.log10( + DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD, DOUBLE)); + assertEquals(DOUBLE, log.type()); + assertTrue(log.valueOf(valueEnv()).isMissing()); + } + + /** + * Test log2 with int value. + */ + @ParameterizedTest(name = "log10({0})") + @ValueSource(ints = {2, 3}) + public void log2_int_value(Integer v) { + FunctionExpression log = dsl.log2(DSL.literal(v)); + assertEquals(log.type(), DOUBLE); + assertThat( + getDoubleValue(log.valueOf(valueEnv())), + closeTo(Math.log(v) / Math.log(2), 0.0001) + ); + assertEquals(String.format("log2(%s)", v.toString()), log.toString()); + } + + /** + * Test log2 with long value. + */ + @ParameterizedTest(name = "log10({0})") + @ValueSource(longs = {2L, 3L}) + public void log2_long_value(Long v) { + FunctionExpression log = dsl.log2(DSL.literal(v)); + assertEquals(log.type(), DOUBLE); + assertThat( + getDoubleValue(log.valueOf(valueEnv())), + closeTo(Math.log(v) / Math.log(2), 0.0001) + ); + assertEquals(String.format("log2(%s)", v.toString()), log.toString()); + } + + /** + * Test log2 with float value. + */ + @ParameterizedTest(name = "log10({0})") + @ValueSource(floats = {2F, 3F}) + public void log2_float_value(Float v) { + FunctionExpression log = dsl.log2(DSL.literal(v)); + assertEquals(log.type(), DOUBLE); + assertThat( + getDoubleValue(log.valueOf(valueEnv())), + closeTo(Math.log(v) / Math.log(2), 0.0001) + ); + assertEquals(String.format("log2(%s)", v.toString()), log.toString()); + } + + /** + * Test log2 with double value. + */ + @ParameterizedTest(name = "log10({0})") + @ValueSource(doubles = {2D, 3D}) + public void log2_double_value(Double v) { + FunctionExpression log = dsl.log2(DSL.literal(v)); + assertEquals(log.type(), DOUBLE); + assertThat( + getDoubleValue(log.valueOf(valueEnv())), + closeTo(Math.log(v) / Math.log(2), 0.0001) + ); + assertEquals(String.format("log2(%s)", v.toString()), log.toString()); + } + + /** + * Test log2 with null value. + */ + @Test + public void log2_null_value() { + FunctionExpression log = dsl.log2( + DSL.ref(DOUBLE_TYPE_NULL_VALUE_FIELD, DOUBLE)); + assertEquals(DOUBLE, log.type()); + assertTrue(log.valueOf(valueEnv()).isNull()); + } + + /** + * Test log2 with missing value. + */ + @Test + public void log2_missing_value() { + FunctionExpression log = dsl.log2( + DSL.ref(DOUBLE_TYPE_MISSING_VALUE_FIELD, DOUBLE)); + assertEquals(DOUBLE, log.type()); + assertTrue(log.valueOf(valueEnv()).isMissing()); + } } diff --git a/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/ppl/MathematicalFunctionIT.java b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/ppl/MathematicalFunctionIT.java index bc44ed144a..574717c0ec 100644 --- a/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/ppl/MathematicalFunctionIT.java +++ b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/ppl/MathematicalFunctionIT.java @@ -16,9 +16,11 @@ package com.amazon.opendistroforelasticsearch.sql.ppl; import static com.amazon.opendistroforelasticsearch.sql.legacy.TestsConstants.TEST_INDEX_BANK; +import static com.amazon.opendistroforelasticsearch.sql.util.MatcherUtils.closeTo; import static com.amazon.opendistroforelasticsearch.sql.util.MatcherUtils.rows; import static com.amazon.opendistroforelasticsearch.sql.util.MatcherUtils.schema; import static com.amazon.opendistroforelasticsearch.sql.util.MatcherUtils.verifyDataRows; +import static com.amazon.opendistroforelasticsearch.sql.util.MatcherUtils.verifyDataRowsInOrder; import static com.amazon.opendistroforelasticsearch.sql.util.MatcherUtils.verifySchema; import java.io.IOException; @@ -57,6 +59,18 @@ public void testCeil() throws IOException { rows(32), rows(36), rows(28), rows(33), rows(36), rows(39), rows(34)); } + @Test + public void testCeiling() throws IOException { + JSONObject result = + executeQuery( + String.format( + "source=%s | eval f = ceiling(age) | fields f", TEST_INDEX_BANK)); + verifySchema(result, schema("f", null, "integer")); + verifyDataRows( + result, + rows(32), rows(36), rows(28), rows(33), rows(36), rows(39), rows(34)); + } + @Test public void testExp() throws IOException { JSONObject result = @@ -94,7 +108,20 @@ result, rows(Math.log(32)), rows(Math.log(36)), rows(Math.log(28)), rows(Math.lo } @Test - public void testLog() throws IOException { + public void testLogOneArg() throws IOException { + JSONObject result = + executeQuery( + String.format( + "source=%s | eval f = log(age) | fields f", TEST_INDEX_BANK)); + verifySchema(result, schema("f", null, "double")); + verifyDataRows(result, + closeTo(Math.log(28)), closeTo(Math.log(32)), closeTo(Math.log(33)), closeTo(Math.log(34)), + closeTo(Math.log(36)), closeTo(Math.log(36)), closeTo(Math.log(39)) + ); + } + + @Test + public void testLogTwoArgs() throws IOException { JSONObject result = executeQuery( String.format( @@ -106,4 +133,31 @@ result, rows(Math.log(39225) / Math.log(32)), rows(Math.log(5686) / Math.log(36) rows(Math.log(16418) / Math.log(36)), rows(Math.log(40540) / Math.log(39)), rows(Math.log(48086) / Math.log(34))); } + + @Test + public void testLog10() throws IOException { + JSONObject result = + executeQuery( + String.format( + "source=%s | eval f = log10(age) | fields f", TEST_INDEX_BANK)); + verifySchema(result, schema("f", null, "double")); + verifyDataRows( + result, rows(Math.log10(32)), rows(Math.log10(36)), rows(Math.log10(28)), + rows(Math.log10(33)), rows(Math.log10(36)), rows(Math.log10(39)), rows(Math.log10(34))); + } + + @Test + public void testLog2() throws IOException { + JSONObject result = + executeQuery( + String.format( + "source=%s | eval f = log2(age) | fields f", TEST_INDEX_BANK)); + verifySchema(result, schema("f", null, "double")); + verifyDataRows( + result, + rows(Math.log(32) / Math.log(2)), rows(Math.log(36) / Math.log(2)), + rows(Math.log(28) / Math.log(2)), rows(Math.log(33) / Math.log(2)), + rows(Math.log(36) / Math.log(2)), rows(Math.log(39) / Math.log(2)), + rows(Math.log(34) / Math.log(2))); + } } diff --git a/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/util/MatcherUtils.java b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/util/MatcherUtils.java index 0668b06641..de6af08925 100644 --- a/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/util/MatcherUtils.java +++ b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/util/MatcherUtils.java @@ -18,6 +18,7 @@ import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.arrayContaining; import static org.hamcrest.Matchers.arrayContainingInAnyOrder; +import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsInRelativeOrder; @@ -40,6 +41,7 @@ import org.hamcrest.FeatureMatcher; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; +import org.hamcrest.number.IsCloseTo; import org.json.JSONArray; import org.json.JSONObject; @@ -142,6 +144,10 @@ public static void verifyDataRows(JSONObject response, Matcher... mat verify(response.getJSONArray("datarows"), matchers); } + public static void verifyDataRows(JSONObject response, IsCloseTo... matchers) { + verify(response.getJSONArray("datarows"), matchers); + } + @SafeVarargs public static void verifyColumn(JSONObject response, Matcher... matchers) { verify(response.getJSONArray("schema"), matchers); @@ -233,6 +239,10 @@ protected boolean matchesSafely(JSONArray array) { }; } + public static IsCloseTo closeTo(Number value) { + return new IsCloseTo(value.doubleValue(), 1e-10); + } + public static TypeSafeMatcher columnPattern(String regex) { return new TypeSafeMatcher() { @Override diff --git a/ppl/src/main/antlr/OpenDistroPPLLexer.g4 b/ppl/src/main/antlr/OpenDistroPPLLexer.g4 index 3cca57bfa4..68e0061827 100644 --- a/ppl/src/main/antlr/OpenDistroPPLLexer.g4 +++ b/ppl/src/main/antlr/OpenDistroPPLLexer.g4 @@ -136,10 +136,13 @@ DC: 'DC'; // BASIC FUNCTIONS ABS: 'ABS'; CEIL: 'CEIL'; +CEILING: 'CEILING'; EXP: 'EXP'; FLOOR: 'FLOOR'; LN: 'LN'; LOG: 'LOG'; +LOG10: 'LOG10'; +LOG2: 'LOG2'; // LITERALS AND VALUES //STRING_LITERAL: DQUOTA_STRING | SQUOTA_STRING | BQUOTA_STRING; diff --git a/ppl/src/main/antlr/OpenDistroPPLParser.g4 b/ppl/src/main/antlr/OpenDistroPPLParser.g4 index 6cfc5d6987..a4b5abc412 100644 --- a/ppl/src/main/antlr/OpenDistroPPLParser.g4 +++ b/ppl/src/main/antlr/OpenDistroPPLParser.g4 @@ -203,7 +203,7 @@ functionArg ; mathematicalFunctionBase - : ABS | CEIL | EXP | FLOOR | LN | LOG + : ABS | CEIL | CEILING | EXP | FLOOR | LN | LOG | LOG10 | LOG2 ; dateAndTimeFunctionBase diff --git a/sql/src/main/antlr/OpenDistroSQLLexer.g4 b/sql/src/main/antlr/OpenDistroSQLLexer.g4 index 61c13a3a30..a222da2246 100644 --- a/sql/src/main/antlr/OpenDistroSQLLexer.g4 +++ b/sql/src/main/antlr/OpenDistroSQLLexer.g4 @@ -139,6 +139,7 @@ ATAN: 'ATAN'; ATAN2: 'ATAN2'; CBRT: 'CBRT'; CEIL: 'CEIL'; +CEILING: 'CEILING'; CONCAT: 'CONCAT'; CONCAT_WS: 'CONCAT_WS'; COS: 'COS'; diff --git a/sql/src/main/antlr/OpenDistroSQLParser.g4 b/sql/src/main/antlr/OpenDistroSQLParser.g4 index fb2d7eb83e..84652012ad 100644 --- a/sql/src/main/antlr/OpenDistroSQLParser.g4 +++ b/sql/src/main/antlr/OpenDistroSQLParser.g4 @@ -141,7 +141,7 @@ functionCall ; scalarFunctionName - : ABS + : ABS | CEIL | CEILING | EXP | FLOOR | LN | LOG | LOG10 | LOG2 ; functionArgs From 01d6f964c0128c56b99e8fd3591affe80c63e67e Mon Sep 17 00:00:00 2001 From: chloe-zh Date: Fri, 10 Jul 2020 11:42:09 -0700 Subject: [PATCH 07/11] Added comparison test cases --- .../correctness/expressions/functions.txt | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/integ-test/src/test/resources/correctness/expressions/functions.txt b/integ-test/src/test/resources/correctness/expressions/functions.txt index 5b67c88b66..ac253b12fd 100644 --- a/integ-test/src/test/resources/correctness/expressions/functions.txt +++ b/integ-test/src/test/resources/correctness/expressions/functions.txt @@ -4,4 +4,24 @@ abs(1) abs(-1.234) abs(0.0) abs(4.321) -abs(abs(-1.2) * -1) \ No newline at end of file +abs(abs(-1.2) * -1) +ceil(1) +ceil(-1) +ceil(0.0) +ceil(0.4999) +ceil(abs(1)) +exp(0) +exp(1) +exp(-1) +exp(exp(1) + ceil(-1)) +floor(1) +floor(-1) +floor(0.0) +floor(0.4999) +floor(abs(-1)) +log(2) +log(2.1) +log(log(2)) +log10(2) +log10(2.1) +log10(log10(2)) \ No newline at end of file From 520cc968559971cde44fc7c7a6286b6b43a7f643 Mon Sep 17 00:00:00 2001 From: chloe-zh Date: Fri, 10 Jul 2020 12:18:09 -0700 Subject: [PATCH 08/11] changed matcher to closeto --- .../sql/ppl/MathematicalFunctionIT.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/ppl/MathematicalFunctionIT.java b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/ppl/MathematicalFunctionIT.java index 574717c0ec..b462ce6a2f 100644 --- a/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/ppl/MathematicalFunctionIT.java +++ b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/ppl/MathematicalFunctionIT.java @@ -115,8 +115,8 @@ public void testLogOneArg() throws IOException { "source=%s | eval f = log(age) | fields f", TEST_INDEX_BANK)); verifySchema(result, schema("f", null, "double")); verifyDataRows(result, - closeTo(Math.log(28)), closeTo(Math.log(32)), closeTo(Math.log(33)), closeTo(Math.log(34)), - closeTo(Math.log(36)), closeTo(Math.log(36)), closeTo(Math.log(39)) + rows(Math.log(28)), rows(Math.log(32)), rows(Math.log(33)), rows(Math.log(34)), + rows(Math.log(36)), rows(Math.log(36)), rows(Math.log(39)) ); } @@ -128,10 +128,10 @@ public void testLogTwoArgs() throws IOException { "source=%s | eval f = log(age, balance) | fields f", TEST_INDEX_BANK)); verifySchema(result, schema("f", null, "double")); verifyDataRows( - result, rows(Math.log(39225) / Math.log(32)), rows(Math.log(5686) / Math.log(36)), - rows(Math.log(32838) / Math.log(28)), rows(Math.log(4180) / Math.log(33)), - rows(Math.log(16418) / Math.log(36)), rows(Math.log(40540) / Math.log(39)), - rows(Math.log(48086) / Math.log(34))); + result, closeTo(Math.log(39225) / Math.log(32)), closeTo(Math.log(5686) / Math.log(36)), + closeTo(Math.log(32838) / Math.log(28)), closeTo(Math.log(4180) / Math.log(33)), + closeTo(Math.log(16418) / Math.log(36)), closeTo(Math.log(40540) / Math.log(39)), + closeTo(Math.log(48086) / Math.log(34))); } @Test @@ -155,9 +155,9 @@ public void testLog2() throws IOException { verifySchema(result, schema("f", null, "double")); verifyDataRows( result, - rows(Math.log(32) / Math.log(2)), rows(Math.log(36) / Math.log(2)), - rows(Math.log(28) / Math.log(2)), rows(Math.log(33) / Math.log(2)), - rows(Math.log(36) / Math.log(2)), rows(Math.log(39) / Math.log(2)), - rows(Math.log(34) / Math.log(2))); + closeTo(Math.log(32) / Math.log(2)), closeTo(Math.log(36) / Math.log(2)), + closeTo(Math.log(28) / Math.log(2)), closeTo(Math.log(33) / Math.log(2)), + closeTo(Math.log(36) / Math.log(2)), closeTo(Math.log(39) / Math.log(2)), + closeTo(Math.log(34) / Math.log(2))); } } From 529df83c3b0e59fd22b7d37459834df478466f5e Mon Sep 17 00:00:00 2001 From: chloe-zh Date: Fri, 10 Jul 2020 14:19:51 -0700 Subject: [PATCH 09/11] added closeTo matcher --- .../sql/util/MatcherUtils.java | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/util/MatcherUtils.java b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/util/MatcherUtils.java index de6af08925..4b068885d4 100644 --- a/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/util/MatcherUtils.java +++ b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/util/MatcherUtils.java @@ -18,7 +18,6 @@ import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.arrayContaining; import static org.hamcrest.Matchers.arrayContainingInAnyOrder; -import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsInRelativeOrder; @@ -41,7 +40,6 @@ import org.hamcrest.FeatureMatcher; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; -import org.hamcrest.number.IsCloseTo; import org.json.JSONArray; import org.json.JSONObject; @@ -144,10 +142,6 @@ public static void verifyDataRows(JSONObject response, Matcher... mat verify(response.getJSONArray("datarows"), matchers); } - public static void verifyDataRows(JSONObject response, IsCloseTo... matchers) { - verify(response.getJSONArray("datarows"), matchers); - } - @SafeVarargs public static void verifyColumn(JSONObject response, Matcher... matchers) { verify(response.getJSONArray("schema"), matchers); @@ -239,8 +233,29 @@ protected boolean matchesSafely(JSONArray array) { }; } - public static IsCloseTo closeTo(Number value) { - return new IsCloseTo(value.doubleValue(), 1e-10); + public static TypeSafeMatcher closeTo(Number... values) { + final double error = 1e-10; + return new TypeSafeMatcher() { + @Override + protected boolean matchesSafely(JSONArray item) { + List expectedValues = new ArrayList<>(Arrays.asList(values)); + List actualValues = new ArrayList<>(); + item.iterator().forEachRemaining(v -> actualValues.add((Number) v)); + return actualValues.stream() + .map(v -> (valuesAreClose(v, expectedValues.get(actualValues.indexOf(v))))) + .reduce((a, b) -> a && b) + .get(); + } + + @Override + public void describeTo(Description description) { + description.appendText(String.join(",", Arrays.asList().toString())); + } + + private boolean valuesAreClose(Number v1, Number v2) { + return Math.abs(v1.doubleValue() - v2.doubleValue()) <= error; + } + }; } public static TypeSafeMatcher columnPattern(String regex) { From ef44d0bad611273f85882da34f4e9d7fa1b5bc9b Mon Sep 17 00:00:00 2001 From: chloe-zh Date: Fri, 10 Jul 2020 14:41:47 -0700 Subject: [PATCH 10/11] Extracted method to compile functions in DSL --- .../sql/expression/DSL.java | 96 ++++++++----------- 1 file changed, 39 insertions(+), 57 deletions(-) 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 0660a8496c..43f881a2bf 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 @@ -19,9 +19,9 @@ import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils; import com.amazon.opendistroforelasticsearch.sql.data.type.ExprType; import com.amazon.opendistroforelasticsearch.sql.expression.aggregation.Aggregator; -import com.amazon.opendistroforelasticsearch.sql.expression.env.Environment; import com.amazon.opendistroforelasticsearch.sql.expression.function.BuiltinFunctionName; import com.amazon.opendistroforelasticsearch.sql.expression.function.BuiltinFunctionRepository; +import com.amazon.opendistroforelasticsearch.sql.expression.function.FunctionName; import java.util.Arrays; import lombok.RequiredArgsConstructor; @@ -62,142 +62,124 @@ public static ReferenceExpression ref(String ref, ExprType type) { } public FunctionExpression abs(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.ABS.getName(), Arrays.asList(expressions)); + return function(BuiltinFunctionName.ABS, expressions); } public FunctionExpression ceil(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.CEIL.getName(), Arrays.asList(expressions)); + return function(BuiltinFunctionName.CEIL, expressions); } public FunctionExpression ceiling(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.CEILING.getName(), Arrays.asList(expressions)); + return function(BuiltinFunctionName.CEILING, expressions); } public FunctionExpression exp(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.EXP.getName(), Arrays.asList(expressions)); + return function(BuiltinFunctionName.EXP, expressions); } public FunctionExpression floor(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.FLOOR.getName(), Arrays.asList(expressions)); + return function(BuiltinFunctionName.FLOOR, expressions); } public FunctionExpression ln(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.LN.getName(), Arrays.asList(expressions)); + return function(BuiltinFunctionName.LN, expressions); } public FunctionExpression log(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.LOG.getName(), Arrays.asList(expressions)); + return function(BuiltinFunctionName.LOG, expressions); } public FunctionExpression log10(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.LOG10.getName(), Arrays.asList(expressions)); + return function(BuiltinFunctionName.LOG10, expressions); } public FunctionExpression log2(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.LOG2.getName(), Arrays.asList(expressions)); + return function(BuiltinFunctionName.LOG2, expressions); } public FunctionExpression add(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.ADD.getName(), Arrays.asList(expressions)); + return function(BuiltinFunctionName.ADD, expressions); } public FunctionExpression subtract(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.SUBTRACT.getName(), Arrays.asList(expressions)); + return function(BuiltinFunctionName.SUBTRACT, expressions); } public FunctionExpression multiply(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.MULTIPLY.getName(), Arrays.asList(expressions)); + return function(BuiltinFunctionName.MULTIPLY, expressions); } public FunctionExpression divide(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.DIVIDE.getName(), Arrays.asList(expressions)); + return function(BuiltinFunctionName.DIVIDE, expressions); } public FunctionExpression module(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.MODULES.getName(), Arrays.asList(expressions)); + return function(BuiltinFunctionName.MODULES, expressions); } public FunctionExpression and(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.AND.getName(), Arrays.asList(expressions)); + return function(BuiltinFunctionName.AND, expressions); } public FunctionExpression or(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.OR.getName(), Arrays.asList(expressions)); + return function(BuiltinFunctionName.OR, expressions); } public FunctionExpression xor(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.XOR.getName(), Arrays.asList(expressions)); + return function(BuiltinFunctionName.XOR, expressions); } public FunctionExpression not(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.NOT.getName(), Arrays.asList(expressions)); + return function(BuiltinFunctionName.NOT, expressions); } public FunctionExpression equal(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.EQUAL.getName(), Arrays.asList(expressions)); + return function(BuiltinFunctionName.EQUAL, expressions); } public FunctionExpression notequal(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.NOTEQUAL.getName(), Arrays.asList(expressions)); + return function(BuiltinFunctionName.NOTEQUAL, expressions); } public FunctionExpression less(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.LESS.getName(), Arrays.asList(expressions)); + return function(BuiltinFunctionName.LESS, expressions); } public FunctionExpression lte(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.LTE.getName(), Arrays.asList(expressions)); + return function(BuiltinFunctionName.LTE, expressions); } public FunctionExpression greater(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.GREATER.getName(), Arrays.asList(expressions)); + return function(BuiltinFunctionName.GREATER, expressions); } public FunctionExpression gte(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.GTE.getName(), Arrays.asList(expressions)); + return function(BuiltinFunctionName.GTE, expressions); } public FunctionExpression like(Expression... expressions) { - return (FunctionExpression) - repository.compile(BuiltinFunctionName.LIKE.getName(), Arrays.asList(expressions)); + return function(BuiltinFunctionName.LIKE, expressions); } public Aggregator avg(Expression... expressions) { - return (Aggregator) - repository.compile(BuiltinFunctionName.AVG.getName(), Arrays.asList(expressions)); + return aggregate(BuiltinFunctionName.AVG, expressions); } public Aggregator sum(Expression... expressions) { - return (Aggregator) - repository.compile(BuiltinFunctionName.SUM.getName(), Arrays.asList(expressions)); + return aggregate(BuiltinFunctionName.SUM, expressions); } public Aggregator count(Expression... expressions) { - return (Aggregator) - repository.compile(BuiltinFunctionName.COUNT.getName(), Arrays.asList(expressions)); + return aggregate(BuiltinFunctionName.COUNT, expressions); + } + + private FunctionExpression function(BuiltinFunctionName functionName, Expression... expressions) { + return (FunctionExpression) repository.compile( + functionName.getName(), Arrays.asList(expressions)); + } + + private Aggregator aggregate(BuiltinFunctionName functionName, Expression... expressions) { + return (Aggregator) repository.compile( + functionName.getName(), Arrays.asList(expressions)); } } From af58dbe04c3120ad0795e8b9bdc2afb7498f0fd7 Mon Sep 17 00:00:00 2001 From: chloe-zh Date: Fri, 10 Jul 2020 15:54:55 -0700 Subject: [PATCH 11/11] update --- .../opendistroforelasticsearch/sql/util/MatcherUtils.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/util/MatcherUtils.java b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/util/MatcherUtils.java index 4b068885d4..ee0fc76a31 100644 --- a/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/util/MatcherUtils.java +++ b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/util/MatcherUtils.java @@ -242,9 +242,7 @@ protected boolean matchesSafely(JSONArray item) { List actualValues = new ArrayList<>(); item.iterator().forEachRemaining(v -> actualValues.add((Number) v)); return actualValues.stream() - .map(v -> (valuesAreClose(v, expectedValues.get(actualValues.indexOf(v))))) - .reduce((a, b) -> a && b) - .get(); + .allMatch(v -> valuesAreClose(v, expectedValues.get(actualValues.indexOf(v)))); } @Override