diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java index 3f63268eeaf..b43624bc753 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java @@ -222,6 +222,7 @@ import static org.apache.calcite.sql.fun.SqlLibraryOperators.LEFT; import static org.apache.calcite.sql.fun.SqlLibraryOperators.LEVENSHTEIN; import static org.apache.calcite.sql.fun.SqlLibraryOperators.LOG; +import static org.apache.calcite.sql.fun.SqlLibraryOperators.LOG1P; import static org.apache.calcite.sql.fun.SqlLibraryOperators.LOG2; import static org.apache.calcite.sql.fun.SqlLibraryOperators.LOGICAL_AND; import static org.apache.calcite.sql.fun.SqlLibraryOperators.LOGICAL_OR; @@ -747,6 +748,7 @@ Builder populate() { defineMethod(TANH, BuiltInMethod.TANH.method, NullPolicy.STRICT); defineMethod(TRUNC_BIG_QUERY, BuiltInMethod.STRUNCATE.method, NullPolicy.STRICT); defineMethod(TRUNCATE, BuiltInMethod.STRUNCATE.method, NullPolicy.STRICT); + defineMethod(LOG1P, BuiltInMethod.LOG1P.method, NullPolicy.STRICT); map.put(SAFE_ADD, new SafeArithmeticImplementor(BuiltInMethod.SAFE_ADD.method)); diff --git a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java index ba60ed6e5b1..c6cdc64e650 100644 --- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java +++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java @@ -3061,6 +3061,16 @@ public static double power(BigDecimal b0, BigDecimal b1) { return Math.log(number.doubleValue()) / Math.log(base.doubleValue()); } + /** SQL LOG1P operator applied to double values. */ + public static @Nullable Double log1p(double b0) { + return b0 <= -1 ? null : Math.log1p(b0); + } + + /** SQL LOG1P operator applied to BigDecimal values. */ + public static @Nullable Double log1p(BigDecimal b0) { + return b0.doubleValue() <= -1 ? null : Math.log1p(b0.doubleValue()); + } + // MOD /** SQL MOD operator applied to byte values. */ diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java index e882697af69..bbdaab50963 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java @@ -2525,6 +2525,14 @@ private static RelDataType deriveTypeMapFromEntries(SqlOperatorBinding opBinding OperandTypes.NUMERIC, SqlFunctionCategory.NUMERIC); + /** The "LOG1p(numeric)" function. Returns log(1 + numeric). */ + @LibraryOperator(libraries = {SPARK}) + public static final SqlFunction LOG1P = + SqlBasicFunction.create("LOG1P", + ReturnTypes.DOUBLE_FORCE_NULLABLE, + OperandTypes.NUMERIC, + SqlFunctionCategory.NUMERIC); + @LibraryOperator(libraries = {BIG_QUERY, SPARK}) public static final SqlFunction POW = SqlBasicFunction.create("POW", diff --git a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java index 64aafb11e7c..c5b53555534 100644 --- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java +++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java @@ -558,6 +558,7 @@ public enum BuiltInMethod { SAFE_MULTIPLY(SqlFunctions.class, "safeMultiply", double.class, double.class), SAFE_SUBTRACT(SqlFunctions.class, "safeSubtract", double.class, double.class), LOG(SqlFunctions.class, "log", long.class, long.class, boolean.class), + LOG1P(SqlFunctions.class, "log1p", long.class), SEC(SqlFunctions.class, "sec", double.class), SECH(SqlFunctions.class, "sech", double.class), SIGN(SqlFunctions.class, "sign", long.class), diff --git a/site/_docs/reference.md b/site/_docs/reference.md index a57486c7a26..b7873ca8999 100644 --- a/site/_docs/reference.md +++ b/site/_docs/reference.md @@ -2853,6 +2853,7 @@ In the following: | m s | LOG([, base ], numeric1) | Returns the logarithm of *numeric1* to base *base*, or base e if *base* is not present, or null if *numeric1* is 0 or negative | p | LOG([, base ], numeric1 ) | Returns the logarithm of *numeric1* to base *base*, or base 10 if *numeric1* is not present, or error if *numeric1* is 0 or negative | m s | LOG2(numeric) | Returns the base 2 logarithm of *numeric* +| s | LOG1P(numeric) | Returns the natural logarithm of 1 plus *numeric* | b o p r s | LPAD(string, length [, pattern ]) | Returns a string or bytes value that consists of *string* prepended to *length* with *pattern* | b | TO_BASE32(string) | Converts the *string* to base-32 encoded form and returns an encoded string | b | FROM_BASE32(string) | Returns the decoded result of a base-32 *string* as a string diff --git a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java index 3a04c8f6bb1..825364586cd 100644 --- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java +++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java @@ -7372,6 +7372,31 @@ void checkRegexpExtract(SqlOperatorFixture f0, FunctionAlias functionAlias) { "Cannot take logarithm of zero or negative number", true); } + /** Test case for + * [CALCITE-6549] + * Add LOG1P function (enabled in Spark library). */ + @Test void testLog1PFunc() { + final SqlOperatorFixture f0 = fixture() + .setFor(SqlLibraryOperators.LOG1P, VmName.EXPAND); + f0.checkFails("^log1p(4)^", + "No match found for function signature LOG1P\\(\\)", false); + final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.SPARK); + f.checkScalarApprox("log1p(0)", "DOUBLE", + isWithin(0.0, 0.000001)); + f.checkScalarApprox("log1p(1)", "DOUBLE", + isWithin(0.6931471805599453, 0.000001)); + f.checkScalarApprox("log1p(1e+22)", "DOUBLE", + isWithin(50.65687204586901, 0.000001)); + f.checkScalarApprox("log1p(1.2)", "DOUBLE", + isWithin(0.7884573603642702, 0.000001)); + f.checkScalarApprox("log1p(2.0/3)", "DOUBLE", + isWithin(0.5108256237659907, 0.000001)); + f.checkNull("log1p(cast(null as real))"); + f.checkNull("log1p(-1)"); + f.checkNull("log1p(null)"); + f.checkFails("^log1p()^", INVALID_ARGUMENTS_NUMBER, false); + } + @Test void testRandFunc() { final SqlOperatorFixture f = fixture(); f.setFor(SqlStdOperatorTable.RAND, VmName.EXPAND);