From 2525facd5fb7248def143cadd0943c238eeb2033 Mon Sep 17 00:00:00 2001 From: chloe-zh <fizhang@amazon.com> Date: Tue, 14 Jul 2020 16:34:25 -0700 Subject: [PATCH 01/12] supported conv, crc32, mod, pow/power, round, sign, sqrt, truncate functions --- .../sql/expression/DSL.java | 36 + .../function/BuiltinFunctionName.java | 9 + .../expression/operator/OperatorUtils.java | 52 ++ .../arthmetic/MathematicalFunction.java | 269 +++++++ .../arthmetic/MathematicalFunctionTest.java | 752 ++++++++++++++++++ ppl/src/main/antlr/OpenDistroPPLLexer.g4 | 9 + ppl/src/main/antlr/OpenDistroPPLParser.g4 | 3 +- sql/src/main/antlr/OpenDistroSQLLexer.g4 | 3 + sql/src/main/antlr/OpenDistroSQLParser.g4 | 3 +- 9 files changed, 1134 insertions(+), 2 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 43f881a2bf..0c7f37f7f6 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 @@ -73,6 +73,14 @@ public FunctionExpression ceiling(Expression... expressions) { return function(BuiltinFunctionName.CEILING, expressions); } + public FunctionExpression conv(Expression... expressions) { + return function(BuiltinFunctionName.CONV, expressions); + } + + public FunctionExpression crc32(Expression... expressions) { + return function(BuiltinFunctionName.CRC32, expressions); + } + public FunctionExpression exp(Expression... expressions) { return function(BuiltinFunctionName.EXP, expressions); } @@ -97,6 +105,34 @@ public FunctionExpression log2(Expression... expressions) { return function(BuiltinFunctionName.LOG2, expressions); } + public FunctionExpression mod(Expression... expressions) { + return function(BuiltinFunctionName.MOD, expressions); + } + + public FunctionExpression pow(Expression... expressions) { + return function(BuiltinFunctionName.POW, expressions); + } + + public FunctionExpression power(Expression... expressions) { + return function(BuiltinFunctionName.POWER, expressions); + } + + public FunctionExpression round(Expression... expressions) { + return function(BuiltinFunctionName.ROUND, expressions); + } + + public FunctionExpression sign(Expression... expressions) { + return function(BuiltinFunctionName.SIGN, expressions); + } + + public FunctionExpression sqrt(Expression... expressions) { + return function(BuiltinFunctionName.SQRT, expressions); + } + + public FunctionExpression truncate(Expression... expressions) { + return function(BuiltinFunctionName.TRUNCATE, expressions); + } + public FunctionExpression add(Expression... expressions) { return function(BuiltinFunctionName.ADD, 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 d3fc9c66a0..5f1bc01a4d 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 @@ -18,12 +18,21 @@ public enum BuiltinFunctionName { ABS(FunctionName.of("abs")), CEIL(FunctionName.of("ceil")), CEILING(FunctionName.of("ceiling")), + CONV(FunctionName.of("conv")), + CRC32(FunctionName.of("crc32")), EXP(FunctionName.of("exp")), FLOOR(FunctionName.of("floor")), LN(FunctionName.of("ln")), LOG(FunctionName.of("log")), LOG10(FunctionName.of("log10")), LOG2(FunctionName.of("log2")), + MOD(FunctionName.of("mod")), + POW(FunctionName.of("pow")), + POWER(FunctionName.of("power")), + ROUND(FunctionName.of("round")), + SIGN(FunctionName.of("sign")), + SQRT(FunctionName.of("sqrt")), + TRUNCATE(FunctionName.of("truncate")), /** * Text Functions. 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 52216eb496..26fdbd7974 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 @@ -34,6 +34,54 @@ @UtilityClass public class OperatorUtils { + /** + * Construct {@link FunctionBuilder} which call function with three arguments produced by + * observers.In general, if any operand evaluates to a MISSING value, the enclosing operator + * will return MISSING; if none of operands evaluates to a MISSING value but there is an + * operand evaluates to a NULL value, the enclosing operator will return NULL. + * + * @param functionName function name + * @param function {@link BiFunction} + * @param observer1 extract the value of type T from the first argument + * @param observer2 extract the value of type U from the first argument + * @param observer3 extract the value of type V from the first argument + * @param returnType return type + * @param <T> the type of the first argument to the function + * @param <U> the type of the second argument to the function + * @param <V> the type of the third argument to the function + * @param <R> the type of the result of the function + * @return {@link FunctionBuilder} + */ + public static <T, U, V, R> FunctionBuilder tripleArgFunc( + FunctionName functionName, + TriFunction<T, U, V, R> function, + Function<ExprValue, T> observer1, + Function<ExprValue, U> observer2, + Function<ExprValue, V> observer3, + ExprCoreType returnType) { + return arguments -> new FunctionExpression(functionName, arguments) { + @Override + public ExprValue valueOf(Environment<Expression, ExprValue> valueEnv) { + ExprValue arg1 = arguments.get(0).valueOf(valueEnv); + ExprValue arg2 = arguments.get(1).valueOf(valueEnv); + ExprValue arg3 = arguments.get(2).valueOf(valueEnv); + if (arg1.isMissing() || arg2.isMissing() || arg3.isMissing()) { + return ExprValueUtils.missingValue(); + } else if (arg1.isNull() || arg2.isNull() || arg3.isNull()) { + return ExprValueUtils.nullValue(); + } else { + return ExprValueUtils.fromObjectValue( + function.apply(observer1.apply(arg1), observer2.apply(arg2), observer3.apply(arg3))); + } + } + + @Override + public ExprType type() { + return returnType; + } + }; + } + /** * Construct {@link FunctionBuilder} which call function with arguments produced by observer. * @@ -222,4 +270,8 @@ public String toString() { */ public static final BiPredicate<ExprValue, ExprValue> COMPARE_WITH_NULL_OR_MISSING = (left, right) -> left.isMissing() || right.isMissing() || left.isNull() || right.isNull(); + + public interface TriFunction<T, U, V, R> { + R apply(T t, U u, V v); + } } 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 962ddf15eb..2d540810c4 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 @@ -16,6 +16,7 @@ package com.amazon.opendistroforelasticsearch.sql.expression.operator.arthmetic; import static com.amazon.opendistroforelasticsearch.sql.expression.operator.OperatorUtils.doubleArgFunc; +import static com.amazon.opendistroforelasticsearch.sql.expression.operator.OperatorUtils.tripleArgFunc; import static com.amazon.opendistroforelasticsearch.sql.expression.operator.OperatorUtils.unaryOperator; import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils; @@ -27,10 +28,13 @@ import com.amazon.opendistroforelasticsearch.sql.expression.function.FunctionResolver; import com.amazon.opendistroforelasticsearch.sql.expression.function.FunctionSignature; import com.google.common.collect.ImmutableMap; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.Arrays; import java.util.Map; import java.util.function.BiFunction; import java.util.function.Function; +import java.util.zip.CRC32; import lombok.experimental.UtilityClass; @UtilityClass @@ -44,12 +48,21 @@ public static void register(BuiltinFunctionRepository repository) { repository.register(abs()); repository.register(ceil()); repository.register(ceiling()); + repository.register(conv()); + repository.register(crc32()); repository.register(exp()); repository.register(floor()); repository.register(ln()); repository.register(log()); repository.register(log10()); repository.register(log2()); + repository.register(mod()); + repository.register(pow()); + repository.register(power()); + repository.register(round()); + repository.register(sign()); + repository.register(sqrt()); + repository.register(truncate()); } /** @@ -97,6 +110,50 @@ private static FunctionResolver ceiling() { .build()); } + /** + * Definition of conv(x, a, b) function. + * Convert number x from base a to base b + * The supported signature of floor function is + * (STRING, INTEGER, INTEGER) -> STRING + */ + private static FunctionResolver conv() { + FunctionName functionName = BuiltinFunctionName.CONV.getName(); + return new FunctionResolver( + functionName, + new ImmutableMap.Builder<FunctionSignature, FunctionBuilder>() + .put( + new FunctionSignature(functionName, + Arrays.asList(ExprCoreType.STRING, ExprCoreType.INTEGER, ExprCoreType.INTEGER)), + tripleArgFunc( + functionName, (t, u, v) -> Integer.toString(Integer.parseInt(t, u), v), + ExprValueUtils::getStringValue, ExprValueUtils::getIntegerValue, + ExprValueUtils::getIntegerValue, ExprCoreType.STRING)) + .build()); + } + + /** + * Definition of crc32(x) function. + * Calculate a cyclic redundancy check value and returns a 32-bit unsigned value + * The supported signature of crc32 function is + * STRING -> LONG + */ + private static FunctionResolver crc32() { + FunctionName functionName = BuiltinFunctionName.CRC32.getName(); + return new FunctionResolver(functionName, + new ImmutableMap.Builder<FunctionSignature, FunctionBuilder>() + .put( + new FunctionSignature(functionName, Arrays.asList(ExprCoreType.STRING)), + unaryOperator( + functionName, + v -> { + CRC32 crc = new CRC32(); + crc.update(v.getBytes()); + return crc.getValue(); + }, + ExprValueUtils::getStringValue, ExprCoreType.LONG)) + .build()); + } + /** * Definition of exp(x) function. Calculate exponent function e to the x The supported signature * of exp function is INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE @@ -182,6 +239,186 @@ private static FunctionResolver log2() { singleArgumentFunction(BuiltinFunctionName.LOG2.getName(), v -> Math.log(v) / Math.log(2))); } + /** + * Definition of mod(x, y) function. + * Calculate the remainder of x divided by y + * The supported signature of mod function is + * (INTEGER, INTEGER) -> INTEGER + * (LONG, LONG) -> LONG + * (FLOAT, FLOAT) -> FLOAT + * (DOUBLE, DOUBLE) -> DOUBLE + */ + private static FunctionResolver mod() { + return new FunctionResolver( + BuiltinFunctionName.MOD.getName(), + doubleArgumentsFunction(BuiltinFunctionName.MOD.getName(), + (v1, v2) -> v1 % v2, + (v1, v2) -> v1 % v2)); + } + + /** + * Definition of pow(x, y)/power(x, y) function. + * Calculate the value of x raised to the power of y + * The supported signature of pow/power function is + * (INTEGER, INTEGER) -> INTEGER + * (LONG, LONG) -> LONG + * (FLOAT, FLOAT) -> FLOAT + * (DOUBLE, DOUBLE) -> DOUBLE + */ + private static FunctionResolver pow() { + FunctionName functionName = BuiltinFunctionName.POW.getName(); + return new FunctionResolver(functionName, doubleArgumentsFunction(functionName, Math::pow)); + } + + private static FunctionResolver power() { + FunctionName functionName = BuiltinFunctionName.POWER.getName(); + return new FunctionResolver(functionName, doubleArgumentsFunction(functionName, Math::pow)); + } + + /** + * Definition of round(x)/round(x, d) function. + * Rounds the argument x to d decimal places, d defaults to 0 if not specified. + * The supported signature of round function is + * (x: INTEGER [, y: INTEGER]) -> INTEGER + * (x: LONG [, y: INTEGER]) -> LONG + * (x: FLOAT [, y: INTEGER]) -> FLOAT + * (x: DOUBLE [, y: INTEGER]) -> DOUBLE + */ + private static FunctionResolver round() { + FunctionName functionName = BuiltinFunctionName.ROUND.getName(); + return new FunctionResolver(functionName, + new ImmutableMap.Builder<FunctionSignature, FunctionBuilder>() + .put( + new FunctionSignature(functionName, Arrays.asList(ExprCoreType.INTEGER)), + unaryOperator( + functionName, v -> (long) Math.round(v), ExprValueUtils::getIntegerValue, + ExprCoreType.LONG)) + .put( + new FunctionSignature(functionName, Arrays.asList(ExprCoreType.LONG)), + unaryOperator( + functionName, v -> (long) Math.round(v), ExprValueUtils::getLongValue, + ExprCoreType.LONG)) + .put( + new FunctionSignature(functionName, Arrays.asList(ExprCoreType.FLOAT)), + unaryOperator( + functionName, v -> (double) Math.round(v), ExprValueUtils::getFloatValue, + ExprCoreType.DOUBLE)) + .put( + new FunctionSignature(functionName, Arrays.asList(ExprCoreType.DOUBLE)), + unaryOperator( + functionName, v -> (double) Math.round(v), ExprValueUtils::getDoubleValue, + ExprCoreType.DOUBLE)) + .put( + new FunctionSignature( + functionName, Arrays.asList(ExprCoreType.INTEGER, ExprCoreType.INTEGER)), + doubleArgFunc(functionName, + (v1, v2) -> new BigDecimal(v1).setScale(v2, RoundingMode.HALF_UP).longValue(), + ExprValueUtils::getIntegerValue, ExprValueUtils::getIntegerValue, + ExprCoreType.LONG)) + .put( + new FunctionSignature( + functionName, Arrays.asList(ExprCoreType.LONG, ExprCoreType.INTEGER)), + doubleArgFunc(functionName, + (v1, v2) -> new BigDecimal(v1).setScale(v2, RoundingMode.HALF_UP).longValue(), + ExprValueUtils::getLongValue, ExprValueUtils::getIntegerValue, + ExprCoreType.LONG)) + .put( + new FunctionSignature( + functionName, Arrays.asList(ExprCoreType.FLOAT, ExprCoreType.INTEGER)), + doubleArgFunc(functionName, + (v1, v2) -> new BigDecimal(v1).setScale(v2, RoundingMode.HALF_UP).doubleValue(), + ExprValueUtils::getFloatValue, ExprValueUtils::getIntegerValue, + ExprCoreType.DOUBLE)) + .put( + new FunctionSignature( + functionName, Arrays.asList(ExprCoreType.DOUBLE, ExprCoreType.INTEGER)), + doubleArgFunc(functionName, + (v1, v2) -> new BigDecimal(v1).setScale(v2, RoundingMode.HALF_UP).doubleValue(), + ExprValueUtils::getDoubleValue, ExprValueUtils::getIntegerValue, + ExprCoreType.DOUBLE)) + .build()); + } + + /** + * Definition of sign(x) function. + * Returns the sign of the argument as -1, 0, or 1 + * depending on whether x is negative, zero, or positive + * The supported signature is + * INTEGER/LONG/FLOAT/DOUBLE -> INTEGER + */ + private static FunctionResolver sign() { + FunctionName functionName = BuiltinFunctionName.SIGN.getName(); + return new FunctionResolver( + functionName, + new ImmutableMap.Builder<FunctionSignature, FunctionBuilder>() + .put( + new FunctionSignature( + functionName, Arrays.asList(ExprCoreType.DOUBLE)), + unaryOperator( + functionName, v -> (int) Math.signum(v), ExprValueUtils::getDoubleValue, + ExprCoreType.INTEGER)) + .build()); + } + + /** + * Definition of sqrt(x) function. + * Calculate the square root of a non-negative number x + * The supported signature is + * INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE + */ + private static FunctionResolver sqrt() { + FunctionName functionName = BuiltinFunctionName.SQRT.getName(); + return new FunctionResolver( + functionName, + singleArgumentFunction( + functionName, + v -> v < 0 ? null : Math.sqrt(v))); + } + + /** + * Definition of truncate(x, d) function. + * Returns the number x, truncated to d decimal places + * The supported signature of round function is + * (x: INTEGER, y: INTEGER) -> INTEGER + * (x: LONG, y: INTEGER) -> LONG + * (x: FLOAT, y: INTEGER) -> FLOAT + * (x: DOUBLE, y: INTEGER) -> DOUBLE + */ + private static FunctionResolver truncate() { + FunctionName functionName = BuiltinFunctionName.TRUNCATE.getName(); + return new FunctionResolver(functionName, + new ImmutableMap.Builder<FunctionSignature, FunctionBuilder>() + .put( + new FunctionSignature( + functionName, Arrays.asList(ExprCoreType.INTEGER, ExprCoreType.INTEGER)), + doubleArgFunc(functionName, + (v1, v2) -> new BigDecimal(v1).setScale(v2, RoundingMode.DOWN).longValue(), + ExprValueUtils::getIntegerValue, ExprValueUtils::getIntegerValue, + ExprCoreType.LONG)) + .put( + new FunctionSignature( + functionName, Arrays.asList(ExprCoreType.LONG, ExprCoreType.INTEGER)), + doubleArgFunc(functionName, + (v1, v2) -> new BigDecimal(v1).setScale(v2, RoundingMode.DOWN).longValue(), + ExprValueUtils::getLongValue, ExprValueUtils::getIntegerValue, + ExprCoreType.LONG)) + .put( + new FunctionSignature( + functionName, Arrays.asList(ExprCoreType.FLOAT, ExprCoreType.INTEGER)), + doubleArgFunc(functionName, + (v1, v2) -> new BigDecimal(v1).setScale(v2, RoundingMode.DOWN).doubleValue(), + ExprValueUtils::getFloatValue, ExprValueUtils::getIntegerValue, + ExprCoreType.DOUBLE)) + .put( + new FunctionSignature( + functionName, Arrays.asList(ExprCoreType.DOUBLE, ExprCoreType.INTEGER)), + doubleArgFunc(functionName, + (v1, v2) -> new BigDecimal(v1).setScale(v2, RoundingMode.DOWN).doubleValue(), + ExprValueUtils::getDoubleValue, ExprValueUtils::getIntegerValue, + ExprCoreType.DOUBLE)) + .build()); + } + /** * Util method to generate single argument function bundles. Applicable for INTEGER -> INTEGER * LONG -> LONG FLOAT -> FLOAT DOUBLE -> DOUBLE @@ -221,4 +458,36 @@ private static Map<FunctionSignature, FunctionBuilder> singleArgumentFunction( functionName, doubleFunc, ExprValueUtils::getDoubleValue, ExprCoreType.DOUBLE)) .build(); } + + private static Map<FunctionSignature, FunctionBuilder> doubleArgumentsFunction( + FunctionName functionName, + BiFunction<Integer, Integer, Integer> intFunc, + BiFunction<Double, Double, Double> doubleFunc) { + return new ImmutableMap.Builder<FunctionSignature, FunctionBuilder>() + .put( + new FunctionSignature( + functionName, Arrays.asList(ExprCoreType.INTEGER, ExprCoreType.INTEGER)), + doubleArgFunc( + functionName, intFunc, ExprValueUtils::getIntegerValue, + ExprValueUtils::getIntegerValue, ExprCoreType.INTEGER)) + .put( + new FunctionSignature( + functionName, Arrays.asList(ExprCoreType.DOUBLE, ExprCoreType.DOUBLE)), + doubleArgFunc( + functionName, doubleFunc, ExprValueUtils::getDoubleValue, + ExprValueUtils::getDoubleValue, ExprCoreType.DOUBLE)) + .build(); + } + + private static Map<FunctionSignature, FunctionBuilder> doubleArgumentsFunction( + FunctionName functionName, + BiFunction<Double, Double, Double> doubleFunc) { + return new ImmutableMap.Builder<FunctionSignature, FunctionBuilder>() + .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 57e6fa5c7b..07b5b46dbb 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,11 +19,14 @@ 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.config.TestConfig.STRING_TYPE_MISSING_VALUE_FILED; +import static com.amazon.opendistroforelasticsearch.sql.config.TestConfig.STRING_TYPE_NULL_VALUE_FILED; 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; import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.LONG; +import static com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType.STRING; import static com.amazon.opendistroforelasticsearch.sql.utils.MatcherUtils.hasType; import static com.amazon.opendistroforelasticsearch.sql.utils.MatcherUtils.hasValue; import static org.hamcrest.MatcherAssert.assertThat; @@ -35,7 +38,10 @@ import com.amazon.opendistroforelasticsearch.sql.expression.DSL; import com.amazon.opendistroforelasticsearch.sql.expression.ExpressionTestBase; import com.amazon.opendistroforelasticsearch.sql.expression.FunctionExpression; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.stream.Stream; +import java.util.zip.CRC32; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; @@ -226,6 +232,137 @@ public void ceil_missing_value() { assertTrue(ceiling.valueOf(valueEnv()).isMissing()); } + /** + * Test conv from decimal base. + */ + @ParameterizedTest(name = "conv({0})") + @ValueSource(strings = {"1", "0", "-1"}) + public void conv_from_decimal(String value) { + FunctionExpression conv = dsl.conv(DSL.literal(value), DSL.literal(10), DSL.literal(2)); + assertThat( + conv.valueOf(valueEnv()), + allOf(hasType(STRING), hasValue(Integer.toString(Integer.parseInt(value), 2)))); + + conv = dsl.conv(DSL.literal(value), DSL.literal(10), DSL.literal(8)); + assertThat( + conv.valueOf(valueEnv()), + allOf(hasType(STRING), hasValue(Integer.toString(Integer.parseInt(value), 8)))); + + conv = dsl.conv(DSL.literal(value), DSL.literal(10), DSL.literal(16)); + assertThat( + conv.valueOf(valueEnv()), + allOf(hasType(STRING), hasValue(Integer.toString(Integer.parseInt(value), 16)))); + } + + /** + * Test conv to decimal base. + */ + @ParameterizedTest(name = "conv({0})") + @ValueSource(strings = {"11", "0", "11111"}) + public void conv_to_decimal(String value) { + FunctionExpression conv = dsl.conv(DSL.literal(value), DSL.literal(2), DSL.literal(10)); + assertThat( + conv.valueOf(valueEnv()), + allOf(hasType(STRING), hasValue(Integer.toString(Integer.parseInt(value, 2))))); + + conv = dsl.conv(DSL.literal(value), DSL.literal(8), DSL.literal(10)); + assertThat( + conv.valueOf(valueEnv()), + allOf(hasType(STRING), hasValue(Integer.toString(Integer.parseInt(value, 8))))); + + conv = dsl.conv(DSL.literal(value), DSL.literal(16), DSL.literal(10)); + assertThat( + conv.valueOf(valueEnv()), + allOf(hasType(STRING), hasValue(Integer.toString(Integer.parseInt(value, 16))))); + } + + /** + * Test conv with null value. + */ + @Test + public void conv_null_value() { + FunctionExpression conv = dsl.conv( + DSL.ref(STRING_TYPE_NULL_VALUE_FILED, STRING), DSL.literal(10), DSL.literal(2)); + assertEquals(STRING, conv.type()); + assertTrue(conv.valueOf(valueEnv()).isNull()); + + conv = dsl.conv( + DSL.literal("1"), DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER), DSL.literal(2)); + assertEquals(STRING, conv.type()); + assertTrue(conv.valueOf(valueEnv()).isNull()); + + conv = dsl.conv( + DSL.literal("1"), DSL.literal(10), DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER)); + assertEquals(STRING, conv.type()); + assertTrue(conv.valueOf(valueEnv()).isNull()); + } + + /** + * Test conv with missing value. + */ + @Test + public void conv_missing_value() { + FunctionExpression conv = dsl.conv( + DSL.ref(STRING_TYPE_MISSING_VALUE_FILED, STRING), DSL.literal(10), DSL.literal(2)); + assertEquals(STRING, conv.type()); + assertTrue(conv.valueOf(valueEnv()).isMissing()); + + conv = dsl.conv( + DSL.literal("1"), DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER), DSL.literal(2)); + assertEquals(STRING, conv.type()); + assertTrue(conv.valueOf(valueEnv()).isMissing()); + + conv = dsl.conv( + DSL.literal("1"), DSL.literal(10), DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER)); + assertEquals(STRING, conv.type()); + assertTrue(conv.valueOf(valueEnv()).isMissing()); + } + + /** + * Test conv with null and missing values. + */ + @Test + public void conv_null_missing() { + FunctionExpression conv = dsl.conv(DSL.ref(STRING_TYPE_MISSING_VALUE_FILED, STRING), + DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER), DSL.literal(2)); + assertEquals(STRING, conv.type()); + assertTrue(conv.valueOf(valueEnv()).isMissing()); + } + + /** + * Test crc32 with string value. + */ + @ParameterizedTest(name = "crc({0})") + @ValueSource(strings = {"odfe", "sql"}) + public void crc32_string_value(String value) { + FunctionExpression crc = dsl.crc32(DSL.literal(value)); + CRC32 crc32 = new CRC32(); + crc32.update(value.getBytes()); + assertThat( + crc.valueOf(valueEnv()), + allOf(hasType(LONG), hasValue(crc32.getValue()))); + } + + /** + * Test crc32 with null value. + */ + @Test + public void crc32_null_value() { + FunctionExpression crc = dsl.crc32(DSL.ref(STRING_TYPE_NULL_VALUE_FILED, STRING)); + assertEquals(LONG, crc.type()); + assertTrue(crc.valueOf(valueEnv()).isNull()); + } + + /** + * Test crc32 with missing value. + */ + @Test + public void crc32_missing_value() { + FunctionExpression crc = dsl.crc32(DSL.ref(STRING_TYPE_MISSING_VALUE_FILED, STRING)); + assertEquals(LONG, crc.type()); + assertTrue(crc.valueOf(valueEnv()).isMissing()); + } + /** * Test exp with integer value. */ @@ -803,4 +940,619 @@ public void log2_missing_value() { assertEquals(DOUBLE, log.type()); assertTrue(log.valueOf(valueEnv()).isMissing()); } + + /** + * Test mod with integer value. + */ + @ParameterizedTest(name = "mod({0}, {1})") + @MethodSource("testLogIntegerArguments") + public void mod_int_value(Integer v1, Integer v2) { + FunctionExpression mod = dsl.mod(DSL.literal(v1), DSL.literal(v2)); + assertThat( + mod.valueOf(valueEnv()), + allOf(hasType(INTEGER), hasValue(v1 % v2))); + } + + /** + * Test mod with double value. + */ + @ParameterizedTest(name = "mod({0}, {1})") + @MethodSource("testLogDoubleArguments") + public void mod_double_value(Double v1, Double v2) { + FunctionExpression mod = dsl.mod(DSL.literal(v1), DSL.literal(v2)); + assertThat( + mod.valueOf(valueEnv()), + allOf(hasType(DOUBLE), hasValue(v1 % v2))); + } + + /** + * Test mod with null value. + */ + @Test + public void mod_null_value() { + FunctionExpression mod = dsl.mod(DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER), DSL.literal(1)); + assertEquals(INTEGER, mod.type()); + assertTrue(mod.valueOf(valueEnv()).isNull()); + + mod = dsl.mod(DSL.literal(1), DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER)); + assertEquals(INTEGER, mod.type()); + assertTrue(mod.valueOf(valueEnv()).isNull()); + + mod = dsl.mod( + DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER), DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER)); + assertEquals(INTEGER, mod.type()); + assertTrue(mod.valueOf(valueEnv()).isNull()); + } + + /** + * Test mod with missing value. + */ + @Test + public void mod_missing_value() { + FunctionExpression mod = + dsl.mod(DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER), DSL.literal(1)); + assertEquals(INTEGER, mod.type()); + assertTrue(mod.valueOf(valueEnv()).isMissing()); + + mod = dsl.mod(DSL.literal(1), DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER)); + assertEquals(INTEGER, mod.type()); + assertTrue(mod.valueOf(valueEnv()).isMissing()); + + mod = dsl.mod( + DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER), + DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER)); + assertEquals(INTEGER, mod.type()); + assertTrue(mod.valueOf(valueEnv()).isMissing()); + } + + /** + * Test mod with null and missing values. + */ + @Test + public void mod_null_missing() { + FunctionExpression mod = dsl.mod(DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER), + DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER)); + assertEquals(INTEGER, mod.type()); + assertTrue(mod.valueOf(valueEnv()).isMissing()); + + mod = dsl.mod(DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER), + DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER)); + assertEquals(INTEGER, mod.type()); + assertTrue(mod.valueOf(valueEnv()).isMissing()); + } + + /** + * Test pow/power with integer value. + */ + @ParameterizedTest(name = "pow({0}, {1}") + @MethodSource("testLogIntegerArguments") + public void pow_int_value(Integer v1, Integer v2) { + FunctionExpression pow = dsl.pow(DSL.literal(v1), DSL.literal(v2)); + assertThat( + pow.valueOf(valueEnv()), + allOf(hasType(DOUBLE), hasValue(Math.pow(v1, v2)))); + + FunctionExpression power = dsl.power(DSL.literal(v1), DSL.literal(v2)); + assertThat( + power.valueOf(valueEnv()), + allOf(hasType(DOUBLE), hasValue(Math.pow(v1, v2)))); + } + + /** + * Test pow/power with long value. + */ + @ParameterizedTest(name = "pow({0}, {1}") + @MethodSource("testLogLongArguments") + public void pow_long_value(Long v1, Long v2) { + FunctionExpression pow = dsl.pow(DSL.literal(v1), DSL.literal(v2)); + assertThat( + pow.valueOf(valueEnv()), + allOf(hasType(DOUBLE), hasValue(Math.pow(v1, v2)))); + + FunctionExpression power = dsl.power(DSL.literal(v1), DSL.literal(v2)); + assertThat( + power.valueOf(valueEnv()), + allOf(hasType(DOUBLE), hasValue(Math.pow(v1, v2)))); + } + + /** + * Test pow/power with float value. + */ + @ParameterizedTest(name = "pow({0}, {1}") + @MethodSource("testLogFloatArguments") + public void pow_float_value(Float v1, Float v2) { + FunctionExpression pow = dsl.pow(DSL.literal(v1), DSL.literal(v2)); + assertThat( + pow.valueOf(valueEnv()), + allOf(hasType(DOUBLE), hasValue(Math.pow(v1, v2)))); + + FunctionExpression power = dsl.power(DSL.literal(v1), DSL.literal(v2)); + assertThat( + power.valueOf(valueEnv()), + allOf(hasType(DOUBLE), hasValue(Math.pow(v1, v2)))); + } + + /** + * Test pow/power with double value. + */ + @ParameterizedTest(name = "pow({0}, {1}") + @MethodSource("testLogDoubleArguments") + public void pow_double_value(Double v1, Double v2) { + FunctionExpression pow = dsl.pow(DSL.literal(v1), DSL.literal(v2)); + assertThat( + pow.valueOf(valueEnv()), + allOf(hasType(DOUBLE), hasValue(Math.pow(v1, v2)))); + + FunctionExpression power = dsl.power(DSL.literal(v1), DSL.literal(v2)); + assertThat( + power.valueOf(valueEnv()), + allOf(hasType(DOUBLE), hasValue(Math.pow(v1, v2)))); + } + + /** + * Test pow/power with null value. + */ + @Test + public void pow_null_value() { + FunctionExpression pow = dsl.pow(DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER), DSL.literal(1)); + assertEquals(DOUBLE, pow.type()); + assertTrue(pow.valueOf(valueEnv()).isNull()); + + dsl.pow(DSL.literal(1), DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER)); + assertEquals(DOUBLE, pow.type()); + assertTrue(pow.valueOf(valueEnv()).isNull()); + + dsl.pow( + DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER), DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER)); + assertEquals(DOUBLE, pow.type()); + assertTrue(pow.valueOf(valueEnv()).isNull()); + + FunctionExpression power = + dsl.power(DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER), DSL.literal(1)); + assertEquals(DOUBLE, power.type()); + assertTrue(power.valueOf(valueEnv()).isNull()); + + power = dsl.power(DSL.literal(1), DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER)); + assertEquals(DOUBLE, power.type()); + assertTrue(power.valueOf(valueEnv()).isNull()); + + power = dsl.power( + DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER), DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER)); + assertEquals(DOUBLE, power.type()); + assertTrue(power.valueOf(valueEnv()).isNull()); + } + + /** + * Test pow/power with missing value. + */ + @Test + public void pow_missing_value() { + FunctionExpression pow = + dsl.pow(DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER), DSL.literal(1)); + assertEquals(DOUBLE, pow.type()); + assertTrue(pow.valueOf(valueEnv()).isMissing()); + + dsl.pow(DSL.literal(1), DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER)); + assertEquals(DOUBLE, pow.type()); + assertTrue(pow.valueOf(valueEnv()).isMissing()); + + dsl.pow(DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER), + DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER)); + assertEquals(DOUBLE, pow.type()); + assertTrue(pow.valueOf(valueEnv()).isMissing()); + + FunctionExpression power = + dsl.power(DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER), DSL.literal(1)); + assertEquals(DOUBLE, power.type()); + assertTrue(power.valueOf(valueEnv()).isMissing()); + + power = dsl.power(DSL.literal(1), DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER)); + assertEquals(DOUBLE, power.type()); + assertTrue(power.valueOf(valueEnv()).isMissing()); + + power = dsl.power(DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER), + DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER)); + assertEquals(DOUBLE, power.type()); + assertTrue(power.valueOf(valueEnv()).isMissing()); + } + + /** + * Test pow/power with null and missing values. + */ + @Test + public void pow_null_missing() { + FunctionExpression pow = dsl.pow( + DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER), + DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER)); + assertEquals(DOUBLE, pow.type()); + assertTrue(pow.valueOf(valueEnv()).isMissing()); + + pow = dsl.pow( + DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER), + DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER)); + assertEquals(DOUBLE, pow.type()); + assertTrue(pow.valueOf(valueEnv()).isMissing()); + + FunctionExpression power = dsl.power( + DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER), + DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER)); + assertEquals(DOUBLE, power.type()); + assertTrue(power.valueOf(valueEnv()).isMissing()); + + power = dsl.power( + DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER), + DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER)); + assertEquals(DOUBLE, power.type()); + assertTrue(power.valueOf(valueEnv()).isMissing()); + } + + /** + * Test round with integer value. + */ + @ParameterizedTest(name = "round({0}") + @ValueSource(ints = {2, -2}) + public void round_int_value(Integer value) { + FunctionExpression round = dsl.round(DSL.literal(value)); + assertThat( + round.valueOf(valueEnv()), + allOf(hasType(LONG), hasValue((long) Math.round(value)))); + + round = dsl.round(DSL.literal(value), DSL.literal(1)); + assertThat( + round.valueOf(valueEnv()), + allOf(hasType(LONG), hasValue( + new BigDecimal(value).setScale(1, RoundingMode.HALF_UP).longValue()))); + } + + /** + * Test round with long value. + */ + @ParameterizedTest(name = "round({0}") + @ValueSource(longs = {2L, -2L}) + public void round_long_value(Long value) { + FunctionExpression round = dsl.round(DSL.literal(value)); + assertThat( + round.valueOf(valueEnv()), + allOf(hasType(LONG), hasValue((long) Math.round(value)))); + + round = dsl.round(DSL.literal(value), DSL.literal(1)); + assertThat( + round.valueOf(valueEnv()), + allOf(hasType(LONG), hasValue( + new BigDecimal(value).setScale(1, RoundingMode.HALF_UP).longValue()))); + } + + /** + * Test round with float value. + */ + @ParameterizedTest(name = "round({0}") + @ValueSource(floats = {2F, -2F}) + public void round_float_value(Float value) { + FunctionExpression round = dsl.round(DSL.literal(value)); + assertThat( + round.valueOf(valueEnv()), + allOf(hasType(DOUBLE), hasValue((double) Math.round(value)))); + + round = dsl.round(DSL.literal(value), DSL.literal(1)); + assertThat( + round.valueOf(valueEnv()), + allOf(hasType(DOUBLE), hasValue( + new BigDecimal(value).setScale(1, RoundingMode.HALF_UP).doubleValue()))); + } + + /** + * Test round with double value. + */ + @ParameterizedTest(name = "round({0}") + @ValueSource(doubles = {2D, -2D}) + public void round_double_value(Double value) { + FunctionExpression round = dsl.round(DSL.literal(value)); + assertThat( + round.valueOf(valueEnv()), + allOf(hasType(DOUBLE), hasValue((double) Math.round(value)))); + + round = dsl.round(DSL.literal(value), DSL.literal(1)); + assertThat( + round.valueOf(valueEnv()), + allOf(hasType(DOUBLE), hasValue( + new BigDecimal(value).setScale(1, RoundingMode.HALF_UP).doubleValue()))); + } + + /** + * Test round with null value. + */ + @Test + public void round_null_value() { + FunctionExpression round = dsl.round(DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER)); + assertEquals(LONG, round.type()); + assertTrue(round.valueOf(valueEnv()).isNull()); + + round = dsl.round(DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER), DSL.literal(1)); + assertEquals(LONG, round.type()); + assertTrue(round.valueOf(valueEnv()).isNull()); + + round = dsl.round(DSL.literal(1), DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER)); + assertEquals(LONG, round.type()); + assertTrue(round.valueOf(valueEnv()).isNull()); + } + + /** + * Test round with null value. + */ + @Test + public void round_missing_value() { + FunctionExpression round = dsl.round(DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER)); + assertEquals(LONG, round.type()); + assertTrue(round.valueOf(valueEnv()).isMissing()); + + round = dsl.round(DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER), DSL.literal(1)); + assertEquals(LONG, round.type()); + assertTrue(round.valueOf(valueEnv()).isMissing()); + + round = dsl.round(DSL.literal(1), DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER)); + assertEquals(LONG, round.type()); + assertTrue(round.valueOf(valueEnv()).isMissing()); + } + + /** + * Test round with null and missing values. + */ + @Test + public void round_null_missing() { + FunctionExpression round = dsl.round( + DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER), + DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER)); + assertEquals(LONG, round.type()); + assertTrue(round.valueOf(valueEnv()).isMissing()); + + round = dsl.round( + DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER), + DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER)); + assertEquals(LONG, round.type()); + assertTrue(round.valueOf(valueEnv()).isMissing()); + } + + /** + * Test sign with integer value. + */ + @ParameterizedTest(name = "sign({0})") + @ValueSource(ints = {2, -2}) + public void sign_int_value(Integer value) { + FunctionExpression sign = dsl.sign(DSL.literal(value)); + assertThat( + sign.valueOf(valueEnv()), + allOf(hasType(INTEGER), hasValue((int) Math.signum(value)))); + } + + /** + * Test sign with long value. + */ + @ParameterizedTest(name = "sign({0})") + @ValueSource(longs = {2L, -2L}) + public void sign_long_value(Long value) { + FunctionExpression sign = dsl.sign(DSL.literal(value)); + assertThat( + sign.valueOf(valueEnv()), + allOf(hasType(INTEGER), hasValue((int) Math.signum(value)))); + } + + /** + * Test sign with float value. + */ + @ParameterizedTest(name = "sign({0})") + @ValueSource(floats = {2F, -2F}) + public void sign_float_value(Float value) { + FunctionExpression sign = dsl.sign(DSL.literal(value)); + assertThat( + sign.valueOf(valueEnv()), + allOf(hasType(INTEGER), hasValue((int) Math.signum(value)))); + } + + /** + * Test sign with double value. + */ + @ParameterizedTest(name = "sign({0})") + @ValueSource(doubles = {2, -2}) + public void sign_double_value(Double value) { + FunctionExpression sign = dsl.sign(DSL.literal(value)); + assertThat( + sign.valueOf(valueEnv()), + allOf(hasType(INTEGER), hasValue((int) Math.signum(value)))); + } + + /** + * Test sign with null value. + */ + @Test + public void sign_null_value() { + FunctionExpression sign = dsl.sign(DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER)); + assertEquals(INTEGER, sign.type()); + assertTrue(sign.valueOf(valueEnv()).isNull()); + } + + /** + * Test sign with missing value. + */ + @Test + public void sign_missing_value() { + FunctionExpression sign = dsl.sign(DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER)); + assertEquals(INTEGER, sign.type()); + assertTrue(sign.valueOf(valueEnv()).isMissing()); + } + + /** + * Test sqrt with int value. + */ + @ParameterizedTest(name = "sqrt({0})") + @ValueSource(ints = {1, 2}) + public void sqrt_int_value(Integer value) { + FunctionExpression sqrt = dsl.sqrt(DSL.literal(value)); + assertThat(sqrt.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue(Math.sqrt(value)))); + } + + /** + * Test sqrt with long value. + */ + @ParameterizedTest(name = "sqrt({0})") + @ValueSource(longs = {1L, 2L}) + public void sqrt_long_value(Long value) { + FunctionExpression sqrt = dsl.sqrt(DSL.literal(value)); + assertThat(sqrt.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue(Math.sqrt(value)))); + } + + /** + * Test sqrt with float value. + */ + @ParameterizedTest(name = "sqrt({0})") + @ValueSource(floats = {1F, 2F}) + public void sqrt_float_value(Float value) { + FunctionExpression sqrt = dsl.sqrt(DSL.literal(value)); + assertThat(sqrt.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue(Math.sqrt(value)))); + } + + /** + * Test sqrt with double value. + */ + @ParameterizedTest(name = "sqrt({0})") + @ValueSource(doubles = {1D, 2D}) + public void sqrt_double_value(Double value) { + FunctionExpression sqrt = dsl.sqrt(DSL.literal(value)); + assertThat(sqrt.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue(Math.sqrt(value)))); + } + + /** + * Test sqrt with negative value. + */ + @ParameterizedTest(name = "sqrt({0})") + @ValueSource(doubles = {-1D, -2D}) + public void sqrt_negative_value(Double value) { + FunctionExpression sqrt = dsl.sqrt(DSL.literal(value)); + assertEquals(DOUBLE, sqrt.type()); + assertTrue(sqrt.valueOf(valueEnv()).isNull()); + } + + /** + * Test sqrt with null value. + */ + @Test + public void sqrt_null_value() { + FunctionExpression sqrt = dsl.sqrt(DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER)); + assertEquals(DOUBLE, sqrt.type()); + assertTrue(sqrt.valueOf(valueEnv()).isNull()); + } + + /** + * Test sqrt with missing value. + */ + @Test + public void sqrt_missing_value() { + FunctionExpression sqrt = dsl.sqrt(DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER)); + assertEquals(DOUBLE, sqrt.type()); + assertTrue(sqrt.valueOf(valueEnv()).isMissing()); + } + + /** + * Test truncate with integer value. + */ + @ParameterizedTest(name = "truncate({0}, {1})") + @ValueSource(ints = {2, -2}) + public void truncate_int_value(Integer value) { + FunctionExpression truncate = dsl.truncate(DSL.literal(value), DSL.literal(1)); + assertThat( + truncate.valueOf(valueEnv()), allOf(hasType(LONG), + hasValue(new BigDecimal(value).setScale(1, RoundingMode.DOWN).longValue()))); + } + + /** + * Test truncate with long value. + */ + @ParameterizedTest(name = "truncate({0}, {1})") + @ValueSource(longs = {2L, -2L}) + public void truncate_long_value(Long value) { + FunctionExpression truncate = dsl.truncate(DSL.literal(value), DSL.literal(1)); + assertThat( + truncate.valueOf(valueEnv()), allOf(hasType(LONG), + hasValue(new BigDecimal(value).setScale(1, RoundingMode.DOWN).longValue()))); + } + + /** + * Test truncate with float value. + */ + @ParameterizedTest(name = "truncate({0}, {1})") + @ValueSource(floats = {2F, -2F}) + public void truncate_float_value(Float value) { + FunctionExpression truncate = dsl.truncate(DSL.literal(value), DSL.literal(1)); + assertThat( + truncate.valueOf(valueEnv()), allOf(hasType(DOUBLE), + hasValue(new BigDecimal(value).setScale(1, RoundingMode.DOWN).doubleValue()))); + } + + /** + * Test truncate with double value. + */ + @ParameterizedTest(name = "truncate({0}, {1})") + @ValueSource(doubles = {2D, -2D}) + public void truncate_double_value(Double value) { + FunctionExpression truncate = dsl.truncate(DSL.literal(value), DSL.literal(1)); + assertThat( + truncate.valueOf(valueEnv()), allOf(hasType(DOUBLE), + hasValue(new BigDecimal(value).setScale(1, RoundingMode.DOWN).doubleValue()))); + } + + /** + * Test truncate with null value. + */ + @Test + public void truncate_null_value() { + FunctionExpression truncate = + dsl.truncate(DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER), DSL.literal(1)); + assertEquals(LONG, truncate.type()); + assertTrue(truncate.valueOf(valueEnv()).isNull()); + + truncate = dsl.truncate(DSL.literal(1), DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER)); + assertEquals(LONG, truncate.type()); + assertTrue(truncate.valueOf(valueEnv()).isNull()); + + truncate = dsl.truncate( + DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER), DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER)); + assertEquals(LONG, truncate.type()); + assertTrue(truncate.valueOf(valueEnv()).isNull()); + } + + /** + * Test truncate with missing value. + */ + @Test + public void truncate_missing_value() { + FunctionExpression truncate = + dsl.truncate(DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER), DSL.literal(1)); + assertEquals(LONG, truncate.type()); + assertTrue(truncate.valueOf(valueEnv()).isMissing()); + + truncate = dsl.truncate(DSL.literal(1), DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER)); + assertEquals(LONG, truncate.type()); + assertTrue(truncate.valueOf(valueEnv()).isMissing()); + + truncate = dsl.truncate( + DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER), + DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER)); + assertEquals(LONG, truncate.type()); + assertTrue(truncate.valueOf(valueEnv()).isMissing()); + } + + /** + * Test truncate with null and missing values. + */ + @Test + public void truncate_null_missing() { + FunctionExpression truncate = dsl.truncate(DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER), + DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER)); + assertEquals(LONG, truncate.type()); + assertTrue(truncate.valueOf(valueEnv()).isMissing()); + + truncate = dsl.truncate(DSL.ref(INT_TYPE_MISSING_VALUE_FIELD, INTEGER), + DSL.ref(INT_TYPE_NULL_VALUE_FIELD, INTEGER)); + assertEquals(LONG, truncate.type()); + assertTrue(truncate.valueOf(valueEnv()).isMissing()); + } } diff --git a/ppl/src/main/antlr/OpenDistroPPLLexer.g4 b/ppl/src/main/antlr/OpenDistroPPLLexer.g4 index 68e0061827..670903b973 100644 --- a/ppl/src/main/antlr/OpenDistroPPLLexer.g4 +++ b/ppl/src/main/antlr/OpenDistroPPLLexer.g4 @@ -137,12 +137,21 @@ DC: 'DC'; ABS: 'ABS'; CEIL: 'CEIL'; CEILING: 'CEILING'; +CONV: 'CONV'; +CRC32: 'CRC32'; EXP: 'EXP'; FLOOR: 'FLOOR'; LN: 'LN'; LOG: 'LOG'; LOG10: 'LOG10'; LOG2: 'LOG2'; +MOD: 'MOD'; +POW: 'POW'; +POWER: 'POWER'; +ROUND: 'ROUND'; +SIGN: 'SIGN'; +SQRT: 'SQRT'; +TRUNCATE: 'TRUNCATE'; // 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 a4b5abc412..dc5bbb10c5 100644 --- a/ppl/src/main/antlr/OpenDistroPPLParser.g4 +++ b/ppl/src/main/antlr/OpenDistroPPLParser.g4 @@ -203,7 +203,8 @@ functionArg ; mathematicalFunctionBase - : ABS | CEIL | CEILING | EXP | FLOOR | LN | LOG | LOG10 | LOG2 + : ABS | CEIL | CEILING | CONV | CRC32 | EXP | FLOOR | LN | LOG | LOG10 | LOG2 | MOD | POW | POWER + | ROUND| SIGN | SQRT | TRUNCATE ; dateAndTimeFunctionBase diff --git a/sql/src/main/antlr/OpenDistroSQLLexer.g4 b/sql/src/main/antlr/OpenDistroSQLLexer.g4 index a222da2246..be7b9dcf97 100644 --- a/sql/src/main/antlr/OpenDistroSQLLexer.g4 +++ b/sql/src/main/antlr/OpenDistroSQLLexer.g4 @@ -142,9 +142,11 @@ CEIL: 'CEIL'; CEILING: 'CEILING'; CONCAT: 'CONCAT'; CONCAT_WS: 'CONCAT_WS'; +CONV: 'CONV'; COS: 'COS'; COSH: 'COSH'; COT: 'COT'; +CRC32: 'CRC32'; CURDATE: 'CURDATE'; DATE: 'DATE'; DATE_FORMAT: 'DATE_FORMAT'; @@ -188,6 +190,7 @@ SQRT: 'SQRT'; SUBTRACT: 'SUBTRACT'; TAN: 'TAN'; TIMESTAMP: 'TIMESTAMP'; +TRUNCATE: 'TRUNCATE'; UPPER: 'UPPER'; D: 'D'; diff --git a/sql/src/main/antlr/OpenDistroSQLParser.g4 b/sql/src/main/antlr/OpenDistroSQLParser.g4 index 84652012ad..8a1c6a8b63 100644 --- a/sql/src/main/antlr/OpenDistroSQLParser.g4 +++ b/sql/src/main/antlr/OpenDistroSQLParser.g4 @@ -141,7 +141,8 @@ functionCall ; scalarFunctionName - : ABS | CEIL | CEILING | EXP | FLOOR | LN | LOG | LOG10 | LOG2 + : ABS | CEIL | CEILING | CONV | CRC32 | EXP | FLOOR | LN | LOG | LOG10 | LOG2 | MOD | POW | POWER + | SIGN | SQRT | TRUNCATE ; functionArgs From 2066375cd4c819de2d64f2de660c3935572c505e Mon Sep 17 00:00:00 2001 From: chloe-zh <fizhang@amazon.com> Date: Tue, 14 Jul 2020 17:43:40 -0700 Subject: [PATCH 02/12] update --- .../src/test/resources/correctness/expressions/functions.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/integ-test/src/test/resources/correctness/expressions/functions.txt b/integ-test/src/test/resources/correctness/expressions/functions.txt index 923bee962c..c02dcc2f72 100644 --- a/integ-test/src/test/resources/correctness/expressions/functions.txt +++ b/integ-test/src/test/resources/correctness/expressions/functions.txt @@ -10,10 +10,6 @@ ceil(-1) ceil(0.0) ceil(0.4999) ceil(abs(1)) -conv('1', 10, 2) -conv('-1', 10, 2) -conv('0.1', 10, 16) -conv('2C', 16, 10) exp(0) exp(1) exp(-1) From 2d9cc31818940a1ef851ad84f752684cf66a4b0a Mon Sep 17 00:00:00 2001 From: chloe-zh <fizhang@amazon.com> Date: Wed, 15 Jul 2020 14:39:44 -0700 Subject: [PATCH 03/12] added test cases --- .../arthmetic/MathematicalFunction.java | 8 ++ .../arthmetic/MathematicalFunctionTest.java | 80 +++++++++++- .../sql/ppl/MathematicalFunctionIT.java | 117 ++++++++++++++++++ ppl/src/main/antlr/OpenDistroPPLLexer.g4 | 2 +- ppl/src/main/antlr/OpenDistroPPLParser.g4 | 8 +- 5 files changed, 204 insertions(+), 11 deletions(-) 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 2d540810c4..37da823df3 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 @@ -128,6 +128,14 @@ private static FunctionResolver conv() { functionName, (t, u, v) -> Integer.toString(Integer.parseInt(t, u), v), ExprValueUtils::getStringValue, ExprValueUtils::getIntegerValue, ExprValueUtils::getIntegerValue, ExprCoreType.STRING)) + .put( + new FunctionSignature(functionName, + Arrays.asList( + ExprCoreType.INTEGER, ExprCoreType.INTEGER, ExprCoreType.INTEGER)), + tripleArgFunc(functionName, + (t, u, v) -> Integer.toString(Integer.parseInt(t.toString(), u), v), + ExprValueUtils::getIntegerValue, ExprValueUtils::getIntegerValue, + ExprValueUtils::getIntegerValue, ExprCoreType.STRING)) .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 07b5b46dbb..bdeb38a3e0 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 @@ -233,7 +233,7 @@ public void ceil_missing_value() { } /** - * Test conv from decimal base. + * Test conv from decimal base with string as a number. */ @ParameterizedTest(name = "conv({0})") @ValueSource(strings = {"1", "0", "-1"}) @@ -255,7 +255,29 @@ public void conv_from_decimal(String value) { } /** - * Test conv to decimal base. + * Test conv from decimal base with integer as a number. + */ + @ParameterizedTest(name = "conv({0})") + @ValueSource(ints = {1, 0, -1}) + public void conv_from_decimal(Integer value) { + FunctionExpression conv = dsl.conv(DSL.literal(value), DSL.literal(10), DSL.literal(2)); + assertThat( + conv.valueOf(valueEnv()), + allOf(hasType(STRING), hasValue(Integer.toString(value, 2)))); + + conv = dsl.conv(DSL.literal(value), DSL.literal(10), DSL.literal(8)); + assertThat( + conv.valueOf(valueEnv()), + allOf(hasType(STRING), hasValue(Integer.toString(value, 8)))); + + conv = dsl.conv(DSL.literal(value), DSL.literal(10), DSL.literal(16)); + assertThat( + conv.valueOf(valueEnv()), + allOf(hasType(STRING), hasValue(Integer.toString(value, 16)))); + } + + /** + * Test conv to decimal base with string as a number. */ @ParameterizedTest(name = "conv({0})") @ValueSource(strings = {"11", "0", "11111"}) @@ -276,6 +298,28 @@ public void conv_to_decimal(String value) { allOf(hasType(STRING), hasValue(Integer.toString(Integer.parseInt(value, 16))))); } + /** + * Test conv to decimal base with integer as a number. + */ + @ParameterizedTest(name = "conv({0})") + @ValueSource(ints = {11, 0, 11111}) + public void conv_to_decimal(Integer value) { + FunctionExpression conv = dsl.conv(DSL.literal(value), DSL.literal(2), DSL.literal(10)); + assertThat( + conv.valueOf(valueEnv()), + allOf(hasType(STRING), hasValue(Integer.toString(Integer.parseInt(value.toString(), 2))))); + + conv = dsl.conv(DSL.literal(value), DSL.literal(8), DSL.literal(10)); + assertThat( + conv.valueOf(valueEnv()), + allOf(hasType(STRING), hasValue(Integer.toString(Integer.parseInt(value.toString(), 8))))); + + conv = dsl.conv(DSL.literal(value), DSL.literal(16), DSL.literal(10)); + assertThat( + conv.valueOf(valueEnv()), + allOf(hasType(STRING), hasValue(Integer.toString(Integer.parseInt(value.toString(), 16))))); + } + /** * Test conv with null value. */ @@ -1190,7 +1234,7 @@ public void pow_null_missing() { * Test round with integer value. */ @ParameterizedTest(name = "round({0}") - @ValueSource(ints = {2, -2}) + @ValueSource(ints = {21, -21}) public void round_int_value(Integer value) { FunctionExpression round = dsl.round(DSL.literal(value)); assertThat( @@ -1202,13 +1246,19 @@ public void round_int_value(Integer value) { round.valueOf(valueEnv()), allOf(hasType(LONG), hasValue( new BigDecimal(value).setScale(1, RoundingMode.HALF_UP).longValue()))); + + round = dsl.round(DSL.literal(value), DSL.literal(-1)); + assertThat( + round.valueOf(valueEnv()), + allOf(hasType(LONG), hasValue( + new BigDecimal(value).setScale(-1, RoundingMode.HALF_UP).longValue()))); } /** * Test round with long value. */ @ParameterizedTest(name = "round({0}") - @ValueSource(longs = {2L, -2L}) + @ValueSource(longs = {21L, -21L}) public void round_long_value(Long value) { FunctionExpression round = dsl.round(DSL.literal(value)); assertThat( @@ -1220,13 +1270,19 @@ public void round_long_value(Long value) { round.valueOf(valueEnv()), allOf(hasType(LONG), hasValue( new BigDecimal(value).setScale(1, RoundingMode.HALF_UP).longValue()))); + + round = dsl.round(DSL.literal(value), DSL.literal(-1)); + assertThat( + round.valueOf(valueEnv()), + allOf(hasType(LONG), hasValue( + new BigDecimal(value).setScale(-1, RoundingMode.HALF_UP).longValue()))); } /** * Test round with float value. */ @ParameterizedTest(name = "round({0}") - @ValueSource(floats = {2F, -2F}) + @ValueSource(floats = {21F, -21F}) public void round_float_value(Float value) { FunctionExpression round = dsl.round(DSL.literal(value)); assertThat( @@ -1238,13 +1294,19 @@ public void round_float_value(Float value) { round.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue( new BigDecimal(value).setScale(1, RoundingMode.HALF_UP).doubleValue()))); + + round = dsl.round(DSL.literal(value), DSL.literal(-1)); + assertThat( + round.valueOf(valueEnv()), + allOf(hasType(DOUBLE), hasValue( + new BigDecimal(value).setScale(-1, RoundingMode.HALF_UP).doubleValue()))); } /** * Test round with double value. */ @ParameterizedTest(name = "round({0}") - @ValueSource(doubles = {2D, -2D}) + @ValueSource(doubles = {21D, -21D}) public void round_double_value(Double value) { FunctionExpression round = dsl.round(DSL.literal(value)); assertThat( @@ -1256,6 +1318,12 @@ public void round_double_value(Double value) { round.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue( new BigDecimal(value).setScale(1, RoundingMode.HALF_UP).doubleValue()))); + + round = dsl.round(DSL.literal(value), DSL.literal(-1)); + assertThat( + round.valueOf(valueEnv()), + allOf(hasType(DOUBLE), hasValue( + new BigDecimal(value).setScale(-1, RoundingMode.HALF_UP).doubleValue()))); } /** 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 b462ce6a2f..9c9bf42293 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 @@ -160,4 +160,121 @@ public void testLog2() throws IOException { closeTo(Math.log(36) / Math.log(2)), closeTo(Math.log(39) / Math.log(2)), closeTo(Math.log(34) / Math.log(2))); } + + @Test + public void testConv() throws IOException { + JSONObject result = + executeQuery( + String.format( + "source=%s | eval f = conv(age, 10, 16) | fields f", TEST_INDEX_BANK)); + verifySchema(result, schema("f", null, "string")); + verifyDataRows( + result, rows("20"), rows("24"), rows("1c"), rows("21"), + rows("24"), rows("27"), rows("22")); + } + + @Test + public void testCrc32() throws IOException { + JSONObject result = + executeQuery( + String.format( + "source=%s | eval f = crc32(firstname) | fields f", TEST_INDEX_BANK)); + verifySchema(result, schema("f", null, "long")); + verifyDataRows( + result, rows(324249283), rows(3369714977L), rows(1165568529), rows(2293694493L), + rows(3936131563L), rows(256963594), rows(824319315)); + } + + @Test + public void testMod() throws IOException { + JSONObject result = + executeQuery( + String.format( + "source=%s | eval f = mod(age, 10) | fields f", TEST_INDEX_BANK)); + verifySchema(result, schema("f", null, "integer")); + verifyDataRows( + result, rows(2), rows(6), rows(8), rows(3), rows(6), rows(9), rows(4)); + } + + @Test + public void testPow() throws IOException { + JSONObject pow = + executeQuery( + String.format( + "source=%s | eval f = pow(age, 2) | fields f", TEST_INDEX_BANK)); + verifySchema(pow, schema("f", null, "double")); + verifyDataRows( + pow, rows(1024), rows(1296), rows(784), rows(1089), rows(1296), rows(1521), rows(1156)); + + JSONObject power = + executeQuery( + String.format( + "source=%s | eval f = power(age, 2) | fields f", TEST_INDEX_BANK)); + verifySchema(power, schema("f", null, "double")); + verifyDataRows( + power, rows(1024), rows(1296), rows(784), rows(1089), rows(1296), rows(1521), rows(1156)); + + } + + @Test + public void testRound() throws IOException { + JSONObject result = + executeQuery( + String.format( + "source=%s | eval f = round(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)); + + result = + executeQuery( + String.format( + "source=%s | eval f = round(age, -1) | fields f", TEST_INDEX_BANK)); + verifySchema(result, schema("f", null, "long")); + verifyDataRows(result, + rows(30), rows(40), rows(30), rows(30), rows(40), rows(40), rows(30)); + } + + @Test + public void testSign() throws IOException { + JSONObject result = + executeQuery( + String.format( + "source=%s | eval f = sign(age) | fields f", TEST_INDEX_BANK)); + verifySchema(result, schema("f", null, "integer")); + verifyDataRows( + result, rows(1), rows(1), rows(1), rows(1), rows(1), rows(1), rows(1)); + } + + @Test + public void testSqrt() throws IOException { + JSONObject result = + executeQuery( + String.format( + "source=%s | eval f = sqrt(age) | fields f", TEST_INDEX_BANK)); + verifySchema(result, schema("f", null, "double")); + verifyDataRows(result, + rows(5.656854249492381), rows(6), rows(5.291502622129181), + rows(5.744562646538029), rows(6), rows(6.244997998398398), + rows(5.830951894845301)); + } + + @Test + public void testTruncate() throws IOException { + JSONObject result = + executeQuery( + String.format( + "source=%s | eval f = truncate(age, 1) | 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)); + + result = + executeQuery( + String.format( + "source=%s | eval f = truncate(age, -1) | fields f", TEST_INDEX_BANK)); + verifySchema(result, schema("f", null, "long")); + verifyDataRows(result, + rows(30), rows(30), rows(20), rows(30), rows(30), rows(30), rows(30)); + } } diff --git a/ppl/src/main/antlr/OpenDistroPPLLexer.g4 b/ppl/src/main/antlr/OpenDistroPPLLexer.g4 index 670903b973..2d34e9c49f 100644 --- a/ppl/src/main/antlr/OpenDistroPPLLexer.g4 +++ b/ppl/src/main/antlr/OpenDistroPPLLexer.g4 @@ -159,7 +159,7 @@ ID: ID_LITERAL; INTEGER_LITERAL: DEC_DIGIT+; DECIMAL_LITERAL: (DEC_DIGIT+)? '.' DEC_DIGIT+; -fragment ID_LITERAL: [A-Z_$0-9@]*?[A-Z_$\-]+?[A-Z_$\-0-9]*; +fragment ID_LITERAL: [A-Z_]+[A-Z_$0-9@\-]*; DQUOTA_STRING: '"' ( '\\'. | '""' | ~('"'| '\\') )* '"'; SQUOTA_STRING: '\'' ('\\'. | '\'\'' | ~('\'' | '\\'))* '\''; BQUOTA_STRING: '`' ( '\\'. | '``' | ~('`'|'\\'))* '`'; diff --git a/ppl/src/main/antlr/OpenDistroPPLParser.g4 b/ppl/src/main/antlr/OpenDistroPPLParser.g4 index dc5bbb10c5..37d58bb7bc 100644 --- a/ppl/src/main/antlr/OpenDistroPPLParser.g4 +++ b/ppl/src/main/antlr/OpenDistroPPLParser.g4 @@ -227,8 +227,8 @@ binaryOperator /** literals and values*/ literalValue : stringLiteral - | (PLUS | MINUS)? integerLiteral - | (PLUS | MINUS)? decimalLiteral + | integerLiteral + | decimalLiteral | booleanLiteral ; @@ -237,11 +237,11 @@ stringLiteral ; integerLiteral - : INTEGER_LITERAL + : (PLUS | MINUS)? INTEGER_LITERAL ; decimalLiteral - : DECIMAL_LITERAL + : (PLUS | MINUS)? DECIMAL_LITERAL ; booleanLiteral From ba6d9b17a36d9fed2c9950b3b3f38cad840343a1 Mon Sep 17 00:00:00 2001 From: chloe-zh <fizhang@amazon.com> Date: Wed, 15 Jul 2020 16:04:08 -0700 Subject: [PATCH 04/12] update --- .../sql/expression/operator/OperatorUtils.java | 6 ++++++ 1 file changed, 6 insertions(+) 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 26fdbd7974..568cb6a8e3 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 @@ -79,6 +79,12 @@ public ExprValue valueOf(Environment<Expression, ExprValue> valueEnv) { public ExprType type() { return returnType; } + + @Override + public String toString() { + return String.format("%s(%s, %s, %s)", functionName, arguments.get(0).toString(), arguments + .get(1).toString(), arguments.get(2).toString()); + } }; } From 3e15b7b838cd282ade23972e9983dcca31847b80 Mon Sep 17 00:00:00 2001 From: chloe-zh <fizhang@amazon.com> Date: Wed, 15 Jul 2020 20:38:07 -0700 Subject: [PATCH 05/12] added integ test cases --- .../sql/sql/MathematicalFunctionIT.java | 143 ++++++++++++++++++ .../correctness/expressions/functions.txt | 13 ++ ppl/src/main/antlr/OpenDistroPPLParser.g4 | 2 +- sql/src/main/antlr/OpenDistroSQLParser.g4 | 2 +- 4 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/sql/MathematicalFunctionIT.java diff --git a/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/sql/MathematicalFunctionIT.java b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/sql/MathematicalFunctionIT.java new file mode 100644 index 0000000000..cd9a019cef --- /dev/null +++ b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/sql/MathematicalFunctionIT.java @@ -0,0 +1,143 @@ +/* + * 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.sql; + +import static com.amazon.opendistroforelasticsearch.sql.legacy.plugin.RestSqlAction.QUERY_API_ENDPOINT; +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 static com.amazon.opendistroforelasticsearch.sql.util.TestUtils.getResponseBody; + +import com.amazon.opendistroforelasticsearch.sql.legacy.SQLIntegTestCase; +import com.amazon.opendistroforelasticsearch.sql.util.TestUtils; +import java.io.IOException; +import java.util.Locale; +import org.elasticsearch.client.Request; +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.Response; +import org.json.JSONObject; +import org.junit.jupiter.api.Test; + +public class MathematicalFunctionIT extends SQLIntegTestCase { + + @Override + public void init() throws Exception { + super.init(); + TestUtils.enableNewQueryEngine(client()); + } + + @Test + public void testConv() throws IOException { + JSONObject result = executeQuery("select conv(11, 10, 16)"); + verifySchema(result, schema("conv(11, 10, 16)", null, "string")); + verifyDataRows(result, rows("b")); + + result = executeQuery("select conv(11, 16, 10)"); + verifySchema(result, schema("conv(11, 16, 10)", null, "string")); + verifyDataRows(result, rows("17")); + } + + @Test + public void testCrc32() throws IOException { + JSONObject result = executeQuery("select crc32('MySQL')"); + verifySchema(result, schema("crc32(\"MySQL\")", null, "long")); + verifyDataRows(result, rows(3259397556L)); + } + + @Test + public void testMod() throws IOException { + JSONObject result = executeQuery("select mod(3, 2)"); + verifySchema(result, schema("mod(3, 2)", null, "integer")); + verifyDataRows(result, rows(1)); + + result = executeQuery("select mod(3.1, 2)"); + verifySchema(result, schema("mod(3.1, 2)", null, "double")); + verifyDataRows(result, rows(1.1)); + } + + @Test + public void testRound() throws IOException { + JSONObject result = executeQuery("select round(56.78)"); + verifySchema(result, schema("round(56.78)", null, "double")); + verifyDataRows(result, rows(57)); + + result = executeQuery("select round(56.78, 1)"); + verifySchema(result, schema("round(56.78, 1)", null, "double")); + verifyDataRows(result, rows(56.8)); + + result = executeQuery("select round(56.78, -1)"); + verifySchema(result, schema("round(56.78, -1)", null, "double")); + verifyDataRows(result, rows(60)); + + result = executeQuery("select round(-56)"); + verifySchema(result, schema("round(-56)", null, "long")); + verifyDataRows(result, rows(-56)); + + result = executeQuery("select round(-56, 1)"); + verifySchema(result, schema("round(-56, 1)", null, "long")); + verifyDataRows(result, rows(-56)); + + result = executeQuery("select round(-56, -1)"); + verifySchema(result, schema("round(-56, -1)", null, "long")); + verifyDataRows(result, rows(-60)); + } + + /** + * Test sign function with double value. + */ + @Test + public void testSign() throws IOException { + JSONObject result = executeQuery("select sign(1.1)"); + verifySchema(result, schema("sign(1.1)", null, "integer")); + verifyDataRows(result, rows(1)); + + result = executeQuery("select sign(-1.1)"); + verifySchema(result, schema("sign(1.1)", null, "integer")); + verifyDataRows(result, rows(-1)); + } + + @Test + public void testTruncate() throws IOException { + JSONObject result = executeQuery("select truncate(56.78, 1)"); + verifySchema(result, schema("truncate(56.78, 1)", null, "double")); + verifyDataRows(result, rows(56.7)); + + result = executeQuery("select truncate(56.78, -1)"); + verifySchema(result, schema("truncate(56.78, -1)", null, "double")); + verifyDataRows(result, rows(50)); + + result = executeQuery("select truncate(-56, 1)"); + verifySchema(result, schema("truncate(-56, 1)", null, "long")); + verifyDataRows(result, rows(-56)); + + result = executeQuery("select truncate(-56, -1)"); + verifySchema(result, schema("truncate(-56, -1)", null, "long")); + verifyDataRows(result, rows(-50)); + } + + protected JSONObject executeQuery(String query) throws IOException { + Request request = new Request("POST", QUERY_API_ENDPOINT); + request.setJsonEntity(String.format(Locale.ROOT, "{\n" + " \"query\": \"%s\"\n" + "}", query)); + + RequestOptions.Builder restOptionsBuilder = RequestOptions.DEFAULT.toBuilder(); + restOptionsBuilder.addHeader("Content-Type", "application/json"); + request.setOptions(restOptionsBuilder); + + Response response = client().performRequest(request); + return new JSONObject(getResponseBody(response)); + } +} diff --git a/integ-test/src/test/resources/correctness/expressions/functions.txt b/integ-test/src/test/resources/correctness/expressions/functions.txt index c02dcc2f72..9e7090f444 100644 --- a/integ-test/src/test/resources/correctness/expressions/functions.txt +++ b/integ-test/src/test/resources/correctness/expressions/functions.txt @@ -10,6 +10,19 @@ ceil(-1) ceil(0.0) ceil(0.4999) ceil(abs(1)) +power(2, 2) +power(2, -2) +power(2.1, 2) +power(2, -2.1) +power(abs(2), 2) +sign(0) +sign(-1) +sign(1) +sign(abs(1)) +sqrt(0) +sqrt(1) +sqrt(1.1) +sqrt(abs(1)) exp(0) exp(1) exp(-1) diff --git a/ppl/src/main/antlr/OpenDistroPPLParser.g4 b/ppl/src/main/antlr/OpenDistroPPLParser.g4 index 37d58bb7bc..7b393e9a37 100644 --- a/ppl/src/main/antlr/OpenDistroPPLParser.g4 +++ b/ppl/src/main/antlr/OpenDistroPPLParser.g4 @@ -204,7 +204,7 @@ functionArg mathematicalFunctionBase : ABS | CEIL | CEILING | CONV | CRC32 | EXP | FLOOR | LN | LOG | LOG10 | LOG2 | MOD | POW | POWER - | ROUND| SIGN | SQRT | TRUNCATE + | ROUND | SIGN | SQRT | TRUNCATE ; dateAndTimeFunctionBase diff --git a/sql/src/main/antlr/OpenDistroSQLParser.g4 b/sql/src/main/antlr/OpenDistroSQLParser.g4 index f5e37c853b..264576ee9d 100644 --- a/sql/src/main/antlr/OpenDistroSQLParser.g4 +++ b/sql/src/main/antlr/OpenDistroSQLParser.g4 @@ -167,7 +167,7 @@ scalarFunctionName mathematicalFunctionName : ABS | CEIL | CEILING | CONV | CRC32 | EXP | FLOOR | LN | LOG | LOG10 | LOG2 | MOD | POW | POWER - | SIGN | SQRT | TRUNCATE + | ROUND | SIGN | SQRT | TRUNCATE ; dateTimeFunctionName From 4038f5aeddf3435d3c684fc15bb47858bf5cbccf Mon Sep 17 00:00:00 2001 From: chloe-zh <fizhang@amazon.com> Date: Wed, 15 Jul 2020 21:20:54 -0700 Subject: [PATCH 06/12] update --- .../arthmetic/MathematicalFunctionTest.java | 47 +++++++++++++++++++ .../sql/sql/MathematicalFunctionIT.java | 2 +- 2 files changed, 48 insertions(+), 1 deletion(-) 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 bdeb38a3e0..75d391c46c 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 @@ -242,16 +242,19 @@ public void conv_from_decimal(String value) { assertThat( conv.valueOf(valueEnv()), allOf(hasType(STRING), hasValue(Integer.toString(Integer.parseInt(value), 2)))); + assertEquals(String.format("conv(\"%s\", 10, 2)", value), conv.toString()); conv = dsl.conv(DSL.literal(value), DSL.literal(10), DSL.literal(8)); assertThat( conv.valueOf(valueEnv()), allOf(hasType(STRING), hasValue(Integer.toString(Integer.parseInt(value), 8)))); + assertEquals(String.format("conv(\"%s\", 10, 8)", value), conv.toString()); conv = dsl.conv(DSL.literal(value), DSL.literal(10), DSL.literal(16)); assertThat( conv.valueOf(valueEnv()), allOf(hasType(STRING), hasValue(Integer.toString(Integer.parseInt(value), 16)))); + assertEquals(String.format("conv(\"%s\", 10, 16)", value), conv.toString()); } /** @@ -264,16 +267,19 @@ public void conv_from_decimal(Integer value) { assertThat( conv.valueOf(valueEnv()), allOf(hasType(STRING), hasValue(Integer.toString(value, 2)))); + assertEquals(String.format("conv(%s, 10, 2)", value), conv.toString()); conv = dsl.conv(DSL.literal(value), DSL.literal(10), DSL.literal(8)); assertThat( conv.valueOf(valueEnv()), allOf(hasType(STRING), hasValue(Integer.toString(value, 8)))); + assertEquals(String.format("conv(%s, 10, 8)", value), conv.toString()); conv = dsl.conv(DSL.literal(value), DSL.literal(10), DSL.literal(16)); assertThat( conv.valueOf(valueEnv()), allOf(hasType(STRING), hasValue(Integer.toString(value, 16)))); + assertEquals(String.format("conv(%s, 10, 16)", value), conv.toString()); } /** @@ -286,16 +292,19 @@ public void conv_to_decimal(String value) { assertThat( conv.valueOf(valueEnv()), allOf(hasType(STRING), hasValue(Integer.toString(Integer.parseInt(value, 2))))); + assertEquals(String.format("conv(\"%s\", 2, 10)", value), conv.toString()); conv = dsl.conv(DSL.literal(value), DSL.literal(8), DSL.literal(10)); assertThat( conv.valueOf(valueEnv()), allOf(hasType(STRING), hasValue(Integer.toString(Integer.parseInt(value, 8))))); + assertEquals(String.format("conv(\"%s\", 8, 10)", value), conv.toString()); conv = dsl.conv(DSL.literal(value), DSL.literal(16), DSL.literal(10)); assertThat( conv.valueOf(valueEnv()), allOf(hasType(STRING), hasValue(Integer.toString(Integer.parseInt(value, 16))))); + assertEquals(String.format("conv(\"%s\", 16, 10)", value), conv.toString()); } /** @@ -308,16 +317,19 @@ public void conv_to_decimal(Integer value) { assertThat( conv.valueOf(valueEnv()), allOf(hasType(STRING), hasValue(Integer.toString(Integer.parseInt(value.toString(), 2))))); + assertEquals(String.format("conv(%s, 2, 10)", value), conv.toString()); conv = dsl.conv(DSL.literal(value), DSL.literal(8), DSL.literal(10)); assertThat( conv.valueOf(valueEnv()), allOf(hasType(STRING), hasValue(Integer.toString(Integer.parseInt(value.toString(), 8))))); + assertEquals(String.format("conv(%s, 8, 10)", value), conv.toString()); conv = dsl.conv(DSL.literal(value), DSL.literal(16), DSL.literal(10)); assertThat( conv.valueOf(valueEnv()), allOf(hasType(STRING), hasValue(Integer.toString(Integer.parseInt(value.toString(), 16))))); + assertEquals(String.format("conv(%s, 16, 10)", value), conv.toString()); } /** @@ -385,6 +397,7 @@ public void crc32_string_value(String value) { assertThat( crc.valueOf(valueEnv()), allOf(hasType(LONG), hasValue(crc32.getValue()))); + assertEquals(String.format("crc32(\"%s\")", value), crc.toString()); } /** @@ -995,6 +1008,7 @@ public void mod_int_value(Integer v1, Integer v2) { assertThat( mod.valueOf(valueEnv()), allOf(hasType(INTEGER), hasValue(v1 % v2))); + assertEquals(String.format("mod(%s, %s)", v1, v2), mod.toString()); } /** @@ -1007,6 +1021,7 @@ public void mod_double_value(Double v1, Double v2) { assertThat( mod.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue(v1 % v2))); + assertEquals(String.format("mod(%s, %s)", v1, v2), mod.toString()); } /** @@ -1075,11 +1090,13 @@ public void pow_int_value(Integer v1, Integer v2) { assertThat( pow.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue(Math.pow(v1, v2)))); + assertEquals(String.format("pow(%s, %s)", v1, v2), pow.toString()); FunctionExpression power = dsl.power(DSL.literal(v1), DSL.literal(v2)); assertThat( power.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue(Math.pow(v1, v2)))); + assertEquals(String.format("pow(%s, %s)", v1, v2), pow.toString()); } /** @@ -1092,11 +1109,13 @@ public void pow_long_value(Long v1, Long v2) { assertThat( pow.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue(Math.pow(v1, v2)))); + assertEquals(String.format("pow(%s, %s)", v1, v2), pow.toString()); FunctionExpression power = dsl.power(DSL.literal(v1), DSL.literal(v2)); assertThat( power.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue(Math.pow(v1, v2)))); + assertEquals(String.format("pow(%s, %s)", v1, v2), pow.toString()); } /** @@ -1109,11 +1128,13 @@ public void pow_float_value(Float v1, Float v2) { assertThat( pow.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue(Math.pow(v1, v2)))); + assertEquals(String.format("pow(%s, %s)", v1, v2), pow.toString()); FunctionExpression power = dsl.power(DSL.literal(v1), DSL.literal(v2)); assertThat( power.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue(Math.pow(v1, v2)))); + assertEquals(String.format("pow(%s, %s)", v1, v2), pow.toString()); } /** @@ -1126,11 +1147,13 @@ public void pow_double_value(Double v1, Double v2) { assertThat( pow.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue(Math.pow(v1, v2)))); + assertEquals(String.format("pow(%s, %s)", v1, v2), pow.toString()); FunctionExpression power = dsl.power(DSL.literal(v1), DSL.literal(v2)); assertThat( power.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue(Math.pow(v1, v2)))); + assertEquals(String.format("pow(%s, %s)", v1, v2), pow.toString()); } /** @@ -1240,18 +1263,21 @@ public void round_int_value(Integer value) { assertThat( round.valueOf(valueEnv()), allOf(hasType(LONG), hasValue((long) Math.round(value)))); + assertEquals(String.format("round(%s)", value), round.toString()); round = dsl.round(DSL.literal(value), DSL.literal(1)); assertThat( round.valueOf(valueEnv()), allOf(hasType(LONG), hasValue( new BigDecimal(value).setScale(1, RoundingMode.HALF_UP).longValue()))); + assertEquals(String.format("round(%s, 1)", value), round.toString()); round = dsl.round(DSL.literal(value), DSL.literal(-1)); assertThat( round.valueOf(valueEnv()), allOf(hasType(LONG), hasValue( new BigDecimal(value).setScale(-1, RoundingMode.HALF_UP).longValue()))); + assertEquals(String.format("round(%s, -1)", value), round.toString()); } /** @@ -1264,18 +1290,21 @@ public void round_long_value(Long value) { assertThat( round.valueOf(valueEnv()), allOf(hasType(LONG), hasValue((long) Math.round(value)))); + assertEquals(String.format("round(%s)", value), round.toString()); round = dsl.round(DSL.literal(value), DSL.literal(1)); assertThat( round.valueOf(valueEnv()), allOf(hasType(LONG), hasValue( new BigDecimal(value).setScale(1, RoundingMode.HALF_UP).longValue()))); + assertEquals(String.format("round(%s, 1)", value), round.toString()); round = dsl.round(DSL.literal(value), DSL.literal(-1)); assertThat( round.valueOf(valueEnv()), allOf(hasType(LONG), hasValue( new BigDecimal(value).setScale(-1, RoundingMode.HALF_UP).longValue()))); + assertEquals(String.format("round(%s, -1)", value), round.toString()); } /** @@ -1288,18 +1317,21 @@ public void round_float_value(Float value) { assertThat( round.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue((double) Math.round(value)))); + assertEquals(String.format("round(%s)", value), round.toString()); round = dsl.round(DSL.literal(value), DSL.literal(1)); assertThat( round.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue( new BigDecimal(value).setScale(1, RoundingMode.HALF_UP).doubleValue()))); + assertEquals(String.format("round(%s, 1)", value), round.toString()); round = dsl.round(DSL.literal(value), DSL.literal(-1)); assertThat( round.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue( new BigDecimal(value).setScale(-1, RoundingMode.HALF_UP).doubleValue()))); + assertEquals(String.format("round(%s, -1)", value), round.toString()); } /** @@ -1312,18 +1344,21 @@ public void round_double_value(Double value) { assertThat( round.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue((double) Math.round(value)))); + assertEquals(String.format("round(%s)", value), round.toString()); round = dsl.round(DSL.literal(value), DSL.literal(1)); assertThat( round.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue( new BigDecimal(value).setScale(1, RoundingMode.HALF_UP).doubleValue()))); + assertEquals(String.format("round(%s, 1)", value), round.toString()); round = dsl.round(DSL.literal(value), DSL.literal(-1)); assertThat( round.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue( new BigDecimal(value).setScale(-1, RoundingMode.HALF_UP).doubleValue()))); + assertEquals(String.format("round(%s, -1)", value), round.toString()); } /** @@ -1390,6 +1425,7 @@ public void sign_int_value(Integer value) { assertThat( sign.valueOf(valueEnv()), allOf(hasType(INTEGER), hasValue((int) Math.signum(value)))); + assertEquals(String.format("sign(%s)", value), sign.toString()); } /** @@ -1402,6 +1438,7 @@ public void sign_long_value(Long value) { assertThat( sign.valueOf(valueEnv()), allOf(hasType(INTEGER), hasValue((int) Math.signum(value)))); + assertEquals(String.format("sign(%s)", value), sign.toString()); } /** @@ -1414,6 +1451,7 @@ public void sign_float_value(Float value) { assertThat( sign.valueOf(valueEnv()), allOf(hasType(INTEGER), hasValue((int) Math.signum(value)))); + assertEquals(String.format("sign(%s)", value), sign.toString()); } /** @@ -1426,6 +1464,7 @@ public void sign_double_value(Double value) { assertThat( sign.valueOf(valueEnv()), allOf(hasType(INTEGER), hasValue((int) Math.signum(value)))); + assertEquals(String.format("sign(%s)", value), sign.toString()); } /** @@ -1456,6 +1495,7 @@ public void sign_missing_value() { public void sqrt_int_value(Integer value) { FunctionExpression sqrt = dsl.sqrt(DSL.literal(value)); assertThat(sqrt.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue(Math.sqrt(value)))); + assertEquals(String.format("sqrt(%s)", value), sqrt.toString()); } /** @@ -1466,6 +1506,7 @@ public void sqrt_int_value(Integer value) { public void sqrt_long_value(Long value) { FunctionExpression sqrt = dsl.sqrt(DSL.literal(value)); assertThat(sqrt.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue(Math.sqrt(value)))); + assertEquals(String.format("sqrt(%s)", value), sqrt.toString()); } /** @@ -1476,6 +1517,7 @@ public void sqrt_long_value(Long value) { public void sqrt_float_value(Float value) { FunctionExpression sqrt = dsl.sqrt(DSL.literal(value)); assertThat(sqrt.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue(Math.sqrt(value)))); + assertEquals(String.format("sqrt(%s)", value), sqrt.toString()); } /** @@ -1486,6 +1528,7 @@ public void sqrt_float_value(Float value) { public void sqrt_double_value(Double value) { FunctionExpression sqrt = dsl.sqrt(DSL.literal(value)); assertThat(sqrt.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue(Math.sqrt(value)))); + assertEquals(String.format("sqrt(%s)", value), sqrt.toString()); } /** @@ -1529,6 +1572,7 @@ public void truncate_int_value(Integer value) { assertThat( truncate.valueOf(valueEnv()), allOf(hasType(LONG), hasValue(new BigDecimal(value).setScale(1, RoundingMode.DOWN).longValue()))); + assertEquals(String.format("truncate(%s, 1)", value), truncate.toString()); } /** @@ -1541,6 +1585,7 @@ public void truncate_long_value(Long value) { assertThat( truncate.valueOf(valueEnv()), allOf(hasType(LONG), hasValue(new BigDecimal(value).setScale(1, RoundingMode.DOWN).longValue()))); + assertEquals(String.format("truncate(%s, 1)", value), truncate.toString()); } /** @@ -1553,6 +1598,7 @@ public void truncate_float_value(Float value) { assertThat( truncate.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue(new BigDecimal(value).setScale(1, RoundingMode.DOWN).doubleValue()))); + assertEquals(String.format("truncate(%s, 1)", value), truncate.toString()); } /** @@ -1565,6 +1611,7 @@ public void truncate_double_value(Double value) { assertThat( truncate.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue(new BigDecimal(value).setScale(1, RoundingMode.DOWN).doubleValue()))); + assertEquals(String.format("truncate(%s, 1)", value), truncate.toString()); } /** diff --git a/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/sql/MathematicalFunctionIT.java b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/sql/MathematicalFunctionIT.java index cd9a019cef..405d9058b5 100644 --- a/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/sql/MathematicalFunctionIT.java +++ b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/sql/MathematicalFunctionIT.java @@ -106,7 +106,7 @@ public void testSign() throws IOException { verifyDataRows(result, rows(1)); result = executeQuery("select sign(-1.1)"); - verifySchema(result, schema("sign(1.1)", null, "integer")); + verifySchema(result, schema("sign(-1.1)", null, "integer")); verifyDataRows(result, rows(-1)); } From 37131cacad08b453d73cb47df95cba80fbafdb2d Mon Sep 17 00:00:00 2001 From: chloe-zh <fizhang@amazon.com> Date: Thu, 16 Jul 2020 15:15:48 -0700 Subject: [PATCH 07/12] address comments --- .../operator/arthmetic/ArithmeticFunction.java | 16 ++++++++-------- .../operator/arthmetic/MathematicalFunction.java | 12 +++++++----- .../arthmetic/ArithmeticFunctionTest.java | 15 +++++++++++++++ .../arthmetic/MathematicalFunctionTest.java | 10 ++++++++++ 4 files changed, 40 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/ArithmeticFunction.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/ArithmeticFunction.java index 300b89065d..cf296ef056 100644 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/ArithmeticFunction.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/ArithmeticFunction.java @@ -94,10 +94,10 @@ private static FunctionResolver divide() { return new FunctionResolver( BuiltinFunctionName.DIVIDE.getName(), scalarFunction(BuiltinFunctionName.DIVIDE.getName(), - (v1, v2) -> v1 / v2, - (v1, v2) -> v1 / v2, - (v1, v2) -> v1 / v2, - (v1, v2) -> v1 / v2) + (v1, v2) -> v2 == 0 ? null : v1 / v2, + (v1, v2) -> v2 == 0 ? null : v1 / v2, + (v1, v2) -> v2 == 0 ? null : v1 / v2, + (v1, v2) -> v2 == 0 ? null : v1 / v2) ); } @@ -106,10 +106,10 @@ private static FunctionResolver modules() { return new FunctionResolver( BuiltinFunctionName.MODULES.getName(), scalarFunction(BuiltinFunctionName.MODULES.getName(), - (v1, v2) -> v1 % v2, - (v1, v2) -> v1 % v2, - (v1, v2) -> v1 % v2, - (v1, v2) -> v1 % v2) + (v1, v2) -> v2 == 0 ? null : v1 % v2, + (v1, v2) -> v2 == 0 ? null : v1 % v2, + (v1, v2) -> v2 == 0 ? null : v1 % v2, + (v1, v2) -> v2 == 0 ? null : v1 % v2) ); } 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 37da823df3..630180adba 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 @@ -124,8 +124,9 @@ private static FunctionResolver conv() { .put( new FunctionSignature(functionName, Arrays.asList(ExprCoreType.STRING, ExprCoreType.INTEGER, ExprCoreType.INTEGER)), - tripleArgFunc( - functionName, (t, u, v) -> Integer.toString(Integer.parseInt(t, u), v), + tripleArgFunc(functionName, + (num, fromBase, toBase) -> Integer.toString( + Integer.parseInt(num, fromBase), toBase), ExprValueUtils::getStringValue, ExprValueUtils::getIntegerValue, ExprValueUtils::getIntegerValue, ExprCoreType.STRING)) .put( @@ -133,7 +134,8 @@ private static FunctionResolver conv() { Arrays.asList( ExprCoreType.INTEGER, ExprCoreType.INTEGER, ExprCoreType.INTEGER)), tripleArgFunc(functionName, - (t, u, v) -> Integer.toString(Integer.parseInt(t.toString(), u), v), + (num, fromBase, toBase) -> Integer.toString( + Integer.parseInt(num.toString(), fromBase), toBase), ExprValueUtils::getIntegerValue, ExprValueUtils::getIntegerValue, ExprValueUtils::getIntegerValue, ExprCoreType.STRING)) .build()); @@ -260,8 +262,8 @@ private static FunctionResolver mod() { return new FunctionResolver( BuiltinFunctionName.MOD.getName(), doubleArgumentsFunction(BuiltinFunctionName.MOD.getName(), - (v1, v2) -> v1 % v2, - (v1, v2) -> v1 % v2)); + (v1, v2) -> v2 == 0 ? null : v1 % v2, + (v1, v2) -> v2 == 0 ? null : v1 % v2)); } /** diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/ArithmeticFunctionTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/ArithmeticFunctionTest.java index 002d2f1b19..9bf1fe8020 100644 --- a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/ArithmeticFunctionTest.java +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/ArithmeticFunctionTest.java @@ -24,6 +24,8 @@ import static com.amazon.opendistroforelasticsearch.sql.expression.DSL.literal; import static com.amazon.opendistroforelasticsearch.sql.expression.DSL.ref; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValue; import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils; @@ -42,6 +44,7 @@ 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; @@ -169,6 +172,12 @@ public void divide(ExprValue op1, ExprValue op2) { assertEquals(expectedType, expression.type()); assertValueEqual(BuiltinFunctionName.DIVIDE, expectedType, op1, op2, expression.valueOf(null)); assertEquals(String.format("%s / %s", op1.toString(), op2.toString()), expression.toString()); + + expression = dsl.divide(literal(op1), literal(0)); + expectedType = WideningTypeRule.max(op1.type(), INTEGER); + assertEquals(expectedType, expression.type()); + assertTrue(expression.valueOf(valueEnv()).isNull()); + assertEquals(String.format("%s / 0", op1.toString()), expression.toString()); } @ParameterizedTest(name = "module({1}, {2})") @@ -179,6 +188,12 @@ public void module(ExprValue op1, ExprValue op2) { assertEquals(expectedType, expression.type()); assertValueEqual(BuiltinFunctionName.MODULES, expectedType, op1, op2, expression.valueOf(null)); assertEquals(op1.toString() + " % " + op2.toString(), expression.toString()); + + expression = dsl.module(literal(op1), literal(0)); + expectedType = WideningTypeRule.max(op1.type(), INTEGER); + assertEquals(expectedType, expression.type()); + assertTrue(expression.valueOf(valueEnv()).isNull()); + assertEquals(op1.toString() + " % 0", expression.toString()); } protected void assertValueEqual(BuiltinFunctionName builtinFunctionName, ExprType type, 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 75d391c46c..418e6893db 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 @@ -1024,6 +1024,16 @@ public void mod_double_value(Double v1, Double v2) { assertEquals(String.format("mod(%s, %s)", v1, v2), mod.toString()); } + /** + * Test mod with 0 as divider. + */ + @Test + public void mod_divide_zero() { + FunctionExpression mod = dsl.mod(DSL.literal(1), DSL.literal(0)); + assertEquals(INTEGER, mod.type()); + assertTrue(mod.valueOf(valueEnv()).isNull()); + } + /** * Test mod with null value. */ From aec97aceaa6c1ecdf0f3c4fdeed47b733d12674e Mon Sep 17 00:00:00 2001 From: chloe-zh <fizhang@amazon.com> Date: Thu, 16 Jul 2020 16:09:00 -0700 Subject: [PATCH 08/12] updated java doc, change the behavior of divided by 0, updated sql function doc --- .../arthmetic/MathematicalFunction.java | 22 ++++- docs/user/dql/functions.rst | 89 +++++++++++++++---- .../sql/sql/ExpressionIT.java | 10 +-- 3 files changed, 94 insertions(+), 27 deletions(-) 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 630180adba..0bed318e72 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 @@ -253,15 +253,15 @@ private static FunctionResolver log2() { * Definition of mod(x, y) function. * Calculate the remainder of x divided by y * The supported signature of mod function is - * (INTEGER, INTEGER) -> INTEGER - * (LONG, LONG) -> LONG - * (FLOAT, FLOAT) -> FLOAT - * (DOUBLE, DOUBLE) -> DOUBLE + * (x: INTEGER/LONG/FLOAT/DOUBLE, y: INTEGER/LONG/FLOAT/DOUBLE) + * -> wider type between types of x and y */ private static FunctionResolver mod() { return new FunctionResolver( BuiltinFunctionName.MOD.getName(), doubleArgumentsFunction(BuiltinFunctionName.MOD.getName(), + (v1, v2) -> v2 == 0 ? null : v1 % v2, + (v1, v2) -> v2 == 0 ? null : v1 % v2, (v1, v2) -> v2 == 0 ? null : v1 % v2, (v1, v2) -> v2 == 0 ? null : v1 % v2)); } @@ -472,6 +472,8 @@ private static Map<FunctionSignature, FunctionBuilder> singleArgumentFunction( private static Map<FunctionSignature, FunctionBuilder> doubleArgumentsFunction( FunctionName functionName, BiFunction<Integer, Integer, Integer> intFunc, + BiFunction<Long, Long, Long> longFunc, + BiFunction<Float, Float, Float> floatFunc, BiFunction<Double, Double, Double> doubleFunc) { return new ImmutableMap.Builder<FunctionSignature, FunctionBuilder>() .put( @@ -480,6 +482,18 @@ private static Map<FunctionSignature, FunctionBuilder> doubleArgumentsFunction( doubleArgFunc( functionName, intFunc, ExprValueUtils::getIntegerValue, ExprValueUtils::getIntegerValue, ExprCoreType.INTEGER)) + .put( + new FunctionSignature( + functionName, Arrays.asList(ExprCoreType.LONG, ExprCoreType.LONG)), + doubleArgFunc( + functionName, longFunc, ExprValueUtils::getLongValue, + ExprValueUtils::getLongValue, ExprCoreType.LONG)) + .put( + new FunctionSignature( + functionName, Arrays.asList(ExprCoreType.FLOAT, ExprCoreType.FLOAT)), + doubleArgFunc( + functionName, floatFunc, ExprValueUtils::getFloatValue, + ExprValueUtils::getFloatValue, ExprCoreType.FLOAT)) .put( new FunctionSignature( functionName, Arrays.asList(ExprCoreType.DOUBLE, ExprCoreType.DOUBLE)), diff --git a/docs/user/dql/functions.rst b/docs/user/dql/functions.rst index d048cd10fb..cbcb9c6aa9 100644 --- a/docs/user/dql/functions.rst +++ b/docs/user/dql/functions.rst @@ -139,6 +139,20 @@ Description Specification is undefined and type check is skipped for now + +CONV +==== + +Description +----------- + +Usage: CONV(x, a, b) converts the number x from a base to b base + +Argument type: x: string, a: integer, b: integer + +Return type: string + + COS === @@ -172,6 +186,19 @@ Specifications: 1. COT(NUMBER T) -> DOUBLE +CRC32 +===== + +Description +----------- + +Usage: calculates a cyclic redundancy check value and returns a 32-bit unsigned value + +Argument type: string + +Return type: long + + CURDATE ======= @@ -429,15 +456,17 @@ Specifications: 1. MAKETIME(INTEGER, INTEGER, INTEGER) -> DATE -MODULUS +MOD ======= Description ----------- -Specifications: +Usage: MOD(n, m) calculates the remainder of the number n divided by m -1. MODULUS(NUMBER T, NUMBER) -> T +Argument type: INTEGER/LONG/FLOAT/DOUBLE + +Return type: widen type between types of n and m MONTH @@ -501,10 +530,11 @@ POW Description ----------- -Specifications: +Usage: POW(x, y) calculates the value of x raised to the power of y -1. POW(NUMBER T) -> T -2. POW(NUMBER T, NUMBER) -> T +Argument type: INTEGER/LONG/FLOAT/DOUBLE + +Return type: DOUBLE POWER @@ -513,10 +543,11 @@ POWER Description ----------- -Specifications: +Usage: POWER(x, y) calculates the value of x raised to the power of y + +Argument type: INTEGER/LONG/FLOAT/DOUBLE -1. POWER(NUMBER T) -> T -2. POWER(NUMBER T, NUMBER) -> T +Return type: DOUBLE RADIANS @@ -581,9 +612,14 @@ ROUND Description ----------- -Specifications: +Usage: ROUND(x, d) rounds the argument x to d decimal places, d defaults to 0 if not specified + +Argument type: INTEGER/LONG/FLOAT/DOUBLE + +Return type map: -1. ROUND(NUMBER T) -> T +(INTEGER/LONG [,INTEGER]) -> LONG +(FLOAT/DOUBLE [,INTEGER]) -> LONG RTRIM @@ -603,9 +639,11 @@ SIGN Description ----------- -Specifications: +Usage: returns the sign of the argument as -1, 0, or 1, depending on whether the number is negative, zero, or positive + +Argument type: INTEGER/LONG/FLOAT/DOUBLE -1. SIGN(NUMBER T) -> T +Return type: INTEGER SIGNUM @@ -647,9 +685,14 @@ SQRT Description ----------- -Specifications: +Usage: calculates the square root of a non-negative number + +Argument type: INTEGER/LONG/FLOAT/DOUBLE + +Return type map: -1. SQRT(NUMBER T) -> T +(Non-negative) INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE +(Negative) INTEGER/LONG/FLOAT/DOUBLE -> NULL SUBSTRING @@ -707,6 +750,22 @@ Specifications: 1. TRIM(STRING T) -> T +TRUNCATE +======== + +Description +----------- + +Usage: TRUNCATE(x, d) returns the number x, truncated to d decimal place + +Argument type: INTEGER/LONG/FLOAT/DOUBLE + +Return type map: + +INTEGER/LONG -> LONG +FLOAT/DOUBLE -> DOUBLE + + UPPER ===== diff --git a/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/sql/ExpressionIT.java b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/sql/ExpressionIT.java index 46a0a702a4..754a274f50 100644 --- a/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/sql/ExpressionIT.java +++ b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/sql/ExpressionIT.java @@ -29,6 +29,7 @@ import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.Response; import org.elasticsearch.client.ResponseException; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -38,6 +39,7 @@ * and function expression. Since comparison test in {@link SQLCorrectnessIT} is enforced, * this kind of manual written IT class will be focused on anomaly case test. */ +@Ignore public class ExpressionIT extends RestIntegTestCase { @Rule @@ -49,14 +51,6 @@ protected void init() throws Exception { TestUtils.enableNewQueryEngine(client()); } - @Test - public void testDivideZeroExpression() throws Exception { - expectResponseException().hasStatusCode(500) //TODO: should be client error code 400? - .containsMessage("\"reason\": \"/ by zero\"") - .containsMessage("\"type\": \"ArithmeticException\"") - .whenExecute("SELECT 5 / (1 - 1)"); - } - public ResponseExceptionAssertion expectResponseException() { return new ResponseExceptionAssertion(exceptionRule); } From 76a7f7815fc4572f378e52d446959127a7e85e65 Mon Sep 17 00:00:00 2001 From: chloe-zh <fizhang@amazon.com> Date: Thu, 16 Jul 2020 16:59:09 -0700 Subject: [PATCH 09/12] update --- .../arthmetic/ArithmeticFunctionTest.java | 2 - .../arthmetic/MathematicalFunctionTest.java | 53 ++++++++++++++++--- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/ArithmeticFunctionTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/ArithmeticFunctionTest.java index 9bf1fe8020..877042ef32 100644 --- a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/ArithmeticFunctionTest.java +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/expression/operator/arthmetic/ArithmeticFunctionTest.java @@ -26,7 +26,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; - import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValue; import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils; import com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType; @@ -44,7 +43,6 @@ 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; 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 418e6893db..c4ada14b0d 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,11 +35,16 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValue; +import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValueUtils; import com.amazon.opendistroforelasticsearch.sql.expression.DSL; import com.amazon.opendistroforelasticsearch.sql.expression.ExpressionTestBase; import com.amazon.opendistroforelasticsearch.sql.expression.FunctionExpression; +import com.google.common.collect.Lists; import java.math.BigDecimal; import java.math.RoundingMode; +import java.util.List; +import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.zip.CRC32; import org.junit.jupiter.api.DisplayNameGeneration; @@ -1009,6 +1014,44 @@ public void mod_int_value(Integer v1, Integer v2) { mod.valueOf(valueEnv()), allOf(hasType(INTEGER), hasValue(v1 % v2))); assertEquals(String.format("mod(%s, %s)", v1, v2), mod.toString()); + + mod = dsl.mod(DSL.literal(v1), DSL.literal(0)); + assertEquals(INTEGER, mod.type()); + assertTrue(mod.valueOf(valueEnv()).isNull()); + } + + /** + * Test mod with long value. + */ + @ParameterizedTest(name = "mod({0}, {1})") + @MethodSource("testLogLongArguments") + public void mod_long_value(Long v1, Long v2) { + FunctionExpression mod = dsl.mod(DSL.literal(v1), DSL.literal(v2)); + assertThat( + mod.valueOf(valueEnv()), + allOf(hasType(LONG), hasValue(v1 % v2))); + assertEquals(String.format("mod(%s, %s)", v1, v2), mod.toString()); + + mod = dsl.mod(DSL.literal(v1), DSL.literal(0)); + assertEquals(LONG, mod.type()); + assertTrue(mod.valueOf(valueEnv()).isNull()); + } + + /** + * Test mod with long value. + */ + @ParameterizedTest(name = "mod({0}, {1})") + @MethodSource("testLogFloatArguments") + public void mod_float_value(Float v1, Float v2) { + FunctionExpression mod = dsl.mod(DSL.literal(v1), DSL.literal(v2)); + assertThat( + mod.valueOf(valueEnv()), + allOf(hasType(FLOAT), hasValue(v1 % v2))); + assertEquals(String.format("mod(%s, %s)", v1, v2), mod.toString()); + + mod = dsl.mod(DSL.literal(v1), DSL.literal(0)); + assertEquals(FLOAT, mod.type()); + assertTrue(mod.valueOf(valueEnv()).isNull()); } /** @@ -1022,15 +1065,9 @@ public void mod_double_value(Double v1, Double v2) { mod.valueOf(valueEnv()), allOf(hasType(DOUBLE), hasValue(v1 % v2))); assertEquals(String.format("mod(%s, %s)", v1, v2), mod.toString()); - } - /** - * Test mod with 0 as divider. - */ - @Test - public void mod_divide_zero() { - FunctionExpression mod = dsl.mod(DSL.literal(1), DSL.literal(0)); - assertEquals(INTEGER, mod.type()); + mod = dsl.mod(DSL.literal(v1), DSL.literal(0)); + assertEquals(DOUBLE, mod.type()); assertTrue(mod.valueOf(valueEnv()).isNull()); } From bc58fe39a7840d0d0a7fec3bfe5a5f7aa7aa79d1 Mon Sep 17 00:00:00 2001 From: chloe-zh <fizhang@amazon.com> Date: Thu, 16 Jul 2020 22:07:06 -0700 Subject: [PATCH 10/12] update --- docs/category.json | 3 +- docs/user/dql/functions.rst | 89 +++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/docs/category.json b/docs/category.json index d889ba4a8d..10f47762ea 100644 --- a/docs/category.json +++ b/docs/category.json @@ -15,6 +15,7 @@ "experiment/ppl/cmd/where.rst" ], "sql_cli": [ - "user/dql/expressions.rst" + "user/dql/expressions.rst", + "user/dql/functions.rst" ] } \ No newline at end of file diff --git a/docs/user/dql/functions.rst b/docs/user/dql/functions.rst index cbcb9c6aa9..25e18cf6c6 100644 --- a/docs/user/dql/functions.rst +++ b/docs/user/dql/functions.rst @@ -152,6 +152,15 @@ Argument type: x: string, a: integer, b: integer Return type: string +Example:: + + od> SELECT CONV('12', 10, 16), CONV('2C', 16, 10), CONV(12, 10, 2), CONV(1111, 2, 10) + fetched rows / total rows = 1/1 + +--------------------+--------------------+-----------------+-------------------+ + | CONV('12', 10, 16) | CONV('2C', 16, 10) | CONV(12, 10, 2) | CONV(1111, 2, 10) | + |--------------------|--------------------|-----------------|-------------------| + | "c" | "44" | "1100" | "15" | + +--------------------+--------------------+-----------------+-------------------+ COS === @@ -198,6 +207,16 @@ Argument type: string Return type: long +Example:: + + od> SELECT CRC32('MySQL') + fetched rows / total rows = 1/1 + +----------------+ + | CRC32('MySQL') | + |----------------| + | "c" | + +----------------+ + CURDATE ======= @@ -468,6 +487,16 @@ Argument type: INTEGER/LONG/FLOAT/DOUBLE Return type: widen type between types of n and m +Example:: + + od> SELECT MOD(3, 2), MOD(3.1, 2), MOD(3, 2.1), MOD(3, 0) + fetched rows / total rows = 1/1 + +-----------+-------------+-----------+ + | MOD(3, 2) | MOD(3.6, 2) | MOD(3, 0) | + |-----------|-------------|-----------| + | 1 | 1.6 | null | + +-----------+-------------+-----------+ + MONTH ===== @@ -536,6 +565,16 @@ Argument type: INTEGER/LONG/FLOAT/DOUBLE Return type: DOUBLE +Example:: + + od> SELECT POW(3, 2), POW(-3, 2), POW(3, -2), POW(-3, 0.5) + fetched rows / total rows = 1/1 + +-----------+------------+--------------------+--------------+ + | POW(3, 2) | POW(-3, 2) | POW(3, -2) | POW(-3, 0.5) | + |-----------|------------|--------------------|--------------| + | 9 | 9 | 0.1111111111111111 | null | + +-----------+------------+--------------------+--------------+ + POWER ===== @@ -549,6 +588,16 @@ Argument type: INTEGER/LONG/FLOAT/DOUBLE Return type: DOUBLE +Example:: + + od> SELECT POWER(3, 2), POWER(-3, 2), POWER(3, -2), POWER(-3, 0.5) + fetched rows / total rows = 1/1 + +-------------+--------------+--------------------+----------------+ + | POWER(3, 2) | POWER(-3, 2) | POWER(3, -2) | POWER(-3, 0.5) | + |-------------|--------------|--------------------|----------------| + | 9 | 9 | 0.1111111111111111 | null | + +-------------+--------------+--------------------+----------------+ + RADIANS ======= @@ -621,6 +670,16 @@ Return type map: (INTEGER/LONG [,INTEGER]) -> LONG (FLOAT/DOUBLE [,INTEGER]) -> LONG +Example:: + + od> SELECT ROUND(12.34), ROUND(12.34, 1), ROUND(12.34, -1), ROUND(12, 1) + fetched rows / total rows = 1/1 + +--------------+-----------------+------------------+--------------+ + | ROUND(12.34) | ROUND(12.34, 1) | ROUND(12.34, -1) | ROUND(12, 1) | + |--------------|-----------------|------------------|--------------| + | 12 | 12.3 | 10 | 12 | + +--------------+-----------------+------------------+--------------+ + RTRIM ===== @@ -645,6 +704,16 @@ Argument type: INTEGER/LONG/FLOAT/DOUBLE Return type: INTEGER +Example:: + + od> SELECT SIGN(1), SIGN(0), SIGN(-1.1) + fetched rows / total rows = 1/1 + +---------+---------+------------+ + | SIGN(1) | SIGN(0) | SIGN(-1.1) | + |---------|---------|------------| + | 1 | 0 | 1 | + +---------+---------+------------+ + SIGNUM ====== @@ -694,6 +763,16 @@ Return type map: (Non-negative) INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE (Negative) INTEGER/LONG/FLOAT/DOUBLE -> NULL +Example:: + + od> SELECT SQRT(4), SQRT(4.41), SQRT(-1) + fetched rows / total rows = 1/1 + +---------+------------+----------+ + | SQRT(4) | SQRT(4.41) | SQRT(-1) | + |---------|------------|----------| + | 2 | 2.1 | null | + +---------+------------+----------+ + SUBSTRING ========= @@ -765,6 +844,16 @@ Return type map: INTEGER/LONG -> LONG FLOAT/DOUBLE -> DOUBLE +Example:: + + od> SELECT TRUNCATE(56.78, 1), TRUNCATE(56.78, -1), TRUNCATE(56, 1) + fetched rows / total rows = 1/1 + +--------------------+---------------------+-----------------+ + | TRUNCATE(56.78, 1) | TRUNCATE(56.78, -1) | TRUNCATE(56, 1) | + |--------------------|---------------------|-----------------| + | 56.7 | 50 | 56 | + +--------------------+---------------------+-----------------+ + UPPER ===== From a8962517dd5359111a3b5a67927e880807e80555 Mon Sep 17 00:00:00 2001 From: chloe-zh <fizhang@amazon.com> Date: Fri, 17 Jul 2020 08:12:14 -0700 Subject: [PATCH 11/12] remove test cases that returns null value in doctest --- docs/user/dql/functions.rst | 99 ++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 50 deletions(-) diff --git a/docs/user/dql/functions.rst b/docs/user/dql/functions.rst index 25e18cf6c6..a87b835f6e 100644 --- a/docs/user/dql/functions.rst +++ b/docs/user/dql/functions.rst @@ -156,11 +156,11 @@ Example:: od> SELECT CONV('12', 10, 16), CONV('2C', 16, 10), CONV(12, 10, 2), CONV(1111, 2, 10) fetched rows / total rows = 1/1 - +--------------------+--------------------+-----------------+-------------------+ - | CONV('12', 10, 16) | CONV('2C', 16, 10) | CONV(12, 10, 2) | CONV(1111, 2, 10) | - |--------------------|--------------------|-----------------|-------------------| - | "c" | "44" | "1100" | "15" | - +--------------------+--------------------+-----------------+-------------------+ + +----------------------+----------------------+-------------------+---------------------+ + | conv("12", 10, 16) | conv("2C", 16, 10) | conv(12, 10, 2) | conv(1111, 2, 10) | + |----------------------+----------------------+-------------------+---------------------| + | c | 44 | 1100 | 15 | + +----------------------+----------------------+-------------------+---------------------+ COS === @@ -211,11 +211,11 @@ Example:: od> SELECT CRC32('MySQL') fetched rows / total rows = 1/1 - +----------------+ - | CRC32('MySQL') | - |----------------| - | "c" | - +----------------+ + +------------------+ + | crc32("MySQL") | + |------------------| + | 3259397556 | + +------------------+ CURDATE @@ -489,13 +489,13 @@ Return type: widen type between types of n and m Example:: - od> SELECT MOD(3, 2), MOD(3.1, 2), MOD(3, 2.1), MOD(3, 0) + od> SELECT MOD(3, 2), MOD(3.1, 2) fetched rows / total rows = 1/1 - +-----------+-------------+-----------+ - | MOD(3, 2) | MOD(3.6, 2) | MOD(3, 0) | - |-----------|-------------|-----------| - | 1 | 1.6 | null | - +-----------+-------------+-----------+ + +-------------+---------------+ + | mod(3, 2) | mod(3.1, 2) | + |-------------+---------------| + | 1 | 1.1 | + +-------------+---------------+ MONTH @@ -567,13 +567,13 @@ Return type: DOUBLE Example:: - od> SELECT POW(3, 2), POW(-3, 2), POW(3, -2), POW(-3, 0.5) + od> SELECT POW(3, 2), POW(-3, 2), POW(3, -2) fetched rows / total rows = 1/1 - +-----------+------------+--------------------+--------------+ - | POW(3, 2) | POW(-3, 2) | POW(3, -2) | POW(-3, 0.5) | - |-----------|------------|--------------------|--------------| - | 9 | 9 | 0.1111111111111111 | null | - +-----------+------------+--------------------+--------------+ + +-------------+--------------+--------------------+ + | pow(3, 2) | pow(-3, 2) | pow(3, -2) | + |-------------+--------------+--------------------| + | 9 | 9 | 0.1111111111111111 | + +-------------+--------------+--------------------+ POWER @@ -590,13 +590,13 @@ Return type: DOUBLE Example:: - od> SELECT POWER(3, 2), POWER(-3, 2), POWER(3, -2), POWER(-3, 0.5) + od> SELECT POWER(3, 2), POWER(-3, 2), POWER(3, -2) fetched rows / total rows = 1/1 - +-------------+--------------+--------------------+----------------+ - | POWER(3, 2) | POWER(-3, 2) | POWER(3, -2) | POWER(-3, 0.5) | - |-------------|--------------|--------------------|----------------| - | 9 | 9 | 0.1111111111111111 | null | - +-------------+--------------+--------------------+----------------+ + +---------------+----------------+--------------------+ + | power(3, 2) | power(-3, 2) | power(3, -2) | + |---------------+----------------+--------------------| + | 9 | 9 | 0.1111111111111111 | + +---------------+----------------+--------------------+ RADIANS @@ -674,11 +674,11 @@ Example:: od> SELECT ROUND(12.34), ROUND(12.34, 1), ROUND(12.34, -1), ROUND(12, 1) fetched rows / total rows = 1/1 - +--------------+-----------------+------------------+--------------+ - | ROUND(12.34) | ROUND(12.34, 1) | ROUND(12.34, -1) | ROUND(12, 1) | - |--------------|-----------------|------------------|--------------| - | 12 | 12.3 | 10 | 12 | - +--------------+-----------------+------------------+--------------+ + +----------------+-------------------+--------------------+----------------+ + | round(12.34) | round(12.34, 1) | round(12.34, -1) | round(12, 1) | + |----------------+-------------------+--------------------+----------------| + | 12 | 12.3 | 10 | 12 | + +----------------+-------------------+--------------------+----------------+ RTRIM @@ -708,11 +708,11 @@ Example:: od> SELECT SIGN(1), SIGN(0), SIGN(-1.1) fetched rows / total rows = 1/1 - +---------+---------+------------+ - | SIGN(1) | SIGN(0) | SIGN(-1.1) | - |---------|---------|------------| - | 1 | 0 | 1 | - +---------+---------+------------+ + +-----------+-----------+--------------+ + | sign(1) | sign(0) | sign(-1.1) | + |-----------+-----------+--------------| + | 1 | 0 | -1 | + +-----------+-----------+--------------+ SIGNUM @@ -765,13 +765,13 @@ Return type map: Example:: - od> SELECT SQRT(4), SQRT(4.41), SQRT(-1) + od> SELECT SQRT(4), SQRT(4.41) fetched rows / total rows = 1/1 - +---------+------------+----------+ - | SQRT(4) | SQRT(4.41) | SQRT(-1) | - |---------|------------|----------| - | 2 | 2.1 | null | - +---------+------------+----------+ + +-----------+--------------+ + | sqrt(4) | sqrt(4.41) | + |-----------+--------------| + | 2 | 2.1 | + +-----------+--------------+ SUBSTRING @@ -846,13 +846,12 @@ FLOAT/DOUBLE -> DOUBLE Example:: - od> SELECT TRUNCATE(56.78, 1), TRUNCATE(56.78, -1), TRUNCATE(56, 1) fetched rows / total rows = 1/1 - +--------------------+---------------------+-----------------+ - | TRUNCATE(56.78, 1) | TRUNCATE(56.78, -1) | TRUNCATE(56, 1) | - |--------------------|---------------------|-----------------| - | 56.7 | 50 | 56 | - +--------------------+---------------------+-----------------+ + +----------------------+-----------------------+-------------------+ + | truncate(56.78, 1) | truncate(56.78, -1) | truncate(56, 1) | + |----------------------+-----------------------+-------------------| + | 56.7 | 50 | 56 | + +----------------------+-----------------------+-------------------+ UPPER From b868f74cbceed7be1ef569f97972cdf4fe3b53d6 Mon Sep 17 00:00:00 2001 From: chloe-zh <fizhang@amazon.com> Date: Fri, 17 Jul 2020 08:38:48 -0700 Subject: [PATCH 12/12] added instruction in null value result --- docs/user/dql/functions.rst | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/user/dql/functions.rst b/docs/user/dql/functions.rst index a87b835f6e..2904133b5a 100644 --- a/docs/user/dql/functions.rst +++ b/docs/user/dql/functions.rst @@ -146,11 +146,11 @@ CONV Description ----------- -Usage: CONV(x, a, b) converts the number x from a base to b base +Usage: CONV(x, a, b) converts the number x from a base to b base. -Argument type: x: string, a: integer, b: integer +Argument type: x: STRING, a: INTEGER, b: INTEGER -Return type: string +Return type: STRING Example:: @@ -201,11 +201,11 @@ CRC32 Description ----------- -Usage: calculates a cyclic redundancy check value and returns a 32-bit unsigned value +Usage: Calculates a cyclic redundancy check value and returns a 32-bit unsigned value. -Argument type: string +Argument type: STRING -Return type: long +Return type: LONG Example:: @@ -481,11 +481,11 @@ MOD Description ----------- -Usage: MOD(n, m) calculates the remainder of the number n divided by m +Usage: MOD(n, m) calculates the remainder of the number n divided by m. Argument type: INTEGER/LONG/FLOAT/DOUBLE -Return type: widen type between types of n and m +Return type: Wider type between types of n and m if m is nonzero value. If m equals to 0, then returns NULL. Example:: @@ -559,7 +559,7 @@ POW Description ----------- -Usage: POW(x, y) calculates the value of x raised to the power of y +Usage: POW(x, y) calculates the value of x raised to the power of y. Bad inputs return NULL result. Argument type: INTEGER/LONG/FLOAT/DOUBLE @@ -582,7 +582,7 @@ POWER Description ----------- -Usage: POWER(x, y) calculates the value of x raised to the power of y +Usage: POWER(x, y) calculates the value of x raised to the power of y. Bad inputs return NULL result. Argument type: INTEGER/LONG/FLOAT/DOUBLE @@ -698,7 +698,7 @@ SIGN Description ----------- -Usage: returns the sign of the argument as -1, 0, or 1, depending on whether the number is negative, zero, or positive +Usage: Returns the sign of the argument as -1, 0, or 1, depending on whether the number is negative, zero, or positive Argument type: INTEGER/LONG/FLOAT/DOUBLE @@ -754,7 +754,7 @@ SQRT Description ----------- -Usage: calculates the square root of a non-negative number +Usage: Calculates the square root of a non-negative number Argument type: INTEGER/LONG/FLOAT/DOUBLE