diff --git a/babel/src/test/resources/sql/big-query.iq b/babel/src/test/resources/sql/big-query.iq index 82586abaf1c..0df376e76ac 100755 --- a/babel/src/test/resources/sql/big-query.iq +++ b/babel/src/test/resources/sql/big-query.iq @@ -730,6 +730,68 @@ SELECT SPLIT(x'abc2') as result; Call to function 'SPLIT' with argument of type 'BINARY(2)' requires extra delimiter argument !error +##################################################################### +# LN +# +# LN(x) +# +# Computes the natural logarithm of x. Generates an error if x is less than or +# equal to zero. + +SELECT LN(100) as result; ++-------------------+ +| result | ++-------------------+ +| 4.605170185988092 | ++-------------------+ +(1 row) + +!ok + +##################################################################### +# LOG +# +# LOG(x, y) +# +# If only x is present, LOG is a synonym of LN. If y is also +# present, LOG computes the logarithm of x to base y. +SELECT LOG(64, 8) as result; ++--------+ +| result | ++--------+ +| 2.0 | ++--------+ +(1 row) + +!ok + +SELECT LOG(100) as result; ++-------------------+ +| result | ++-------------------+ +| 4.605170185988092 | ++-------------------+ +(1 row) + +!ok + +##################################################################### +# LOG10 +# +# LOG10(x) +# +# Similar to LOG, but computes logarithm to base 10. + +SELECT LOG10(100) as result; ++--------+ +| result | ++--------+ +| 2.0 | ++--------+ +(1 row) + +!ok + ##################################################################### # STRING # 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 c2cb550cca0..b6832169722 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 @@ -153,6 +153,7 @@ import static org.apache.calcite.sql.fun.SqlLibraryOperators.JSON_STORAGE_SIZE; import static org.apache.calcite.sql.fun.SqlLibraryOperators.JSON_TYPE; import static org.apache.calcite.sql.fun.SqlLibraryOperators.LEFT; +import static org.apache.calcite.sql.fun.SqlLibraryOperators.LOG; import static org.apache.calcite.sql.fun.SqlLibraryOperators.LOGICAL_AND; import static org.apache.calcite.sql.fun.SqlLibraryOperators.LOGICAL_OR; import static org.apache.calcite.sql.fun.SqlLibraryOperators.LPAD; @@ -490,10 +491,12 @@ Builder populate() { defineMethod(MOD, "mod", NullPolicy.STRICT); defineMethod(EXP, "exp", NullPolicy.STRICT); defineMethod(POWER, "power", NullPolicy.STRICT); - defineMethod(LN, "ln", NullPolicy.STRICT); - defineMethod(LOG10, "log10", NullPolicy.STRICT); defineMethod(ABS, "abs", NullPolicy.STRICT); + map.put(LN, new LogImplementor()); + map.put(LOG, new LogImplementor()); + map.put(LOG10, new LogImplementor()); + map.put(RAND, new RandImplementor()); map.put(RAND_INTEGER, new RandIntegerImplementor()); @@ -3684,6 +3687,41 @@ private static class LogicalNotImplementor extends AbstractRexCallImplementor { } } + /** Implementor for the {@code LN}, {@code LOG}, and {@code LOG10} operators. + * + *
Handles all logarithm functions using log rules to determine the
+ * appropriate base (i.e. base e for LN).
+ */
+ private static class LogImplementor extends AbstractRexCallImplementor {
+ LogImplementor() {
+ super("log", NullPolicy.STRICT, true);
+ }
+
+ @Override Expression implementSafe(final RexToLixTranslator translator,
+ final RexCall call, final ListLOG10(numeric)
operator applied to double values. */
- public static double log10(double b0) {
- return Math.log10(b0);
+ /** SQL {@code LOG(number, number2)} function applied to
+ * BigDecimal and double values. */
+ public static double log(BigDecimal d0, double d1) {
+ return Math.log(d0.doubleValue()) / Math.log(d1);
}
- /** SQL {@code LOG10(number)} function applied to BigDecimal values. */
- public static double log10(BigDecimal d) {
- return Math.log10(d.doubleValue());
+ /** SQL {@code LOG(number, number2)} function applied to double values. */
+ public static double log(BigDecimal d0, BigDecimal d1) {
+ return Math.log(d0.doubleValue()) / Math.log(d1.doubleValue());
}
// MOD
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 af3aa62f27a..ae45e899e74 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
@@ -1177,6 +1177,18 @@ static RelDataType deriveTypeSplit(SqlOperatorBinding operatorBinding,
OperandTypes.STRING.or(OperandTypes.BINARY),
SqlFunctionCategory.STRING);
+ /** The "LOG(value [, value2])" function.
+ *
+ * @see SqlStdOperatorTable#LN
+ * @see SqlStdOperatorTable#LOG10
+ */
+ @LibraryOperator(libraries = {BIG_QUERY})
+ public static final SqlFunction LOG =
+ SqlBasicFunction.create("LOG",
+ ReturnTypes.DOUBLE_NULLABLE,
+ OperandTypes.NUMERIC_OPTIONAL_NUMERIC,
+ SqlFunctionCategory.NUMERIC);
+
@LibraryOperator(libraries = {BIG_QUERY})
public static final SqlFunction POW =
SqlStdOperatorTable.POWER.withName("POW");
diff --git a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
index c336f87b84e..b891ba7699a 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
@@ -354,6 +354,11 @@ public static SqlOperandTypeChecker variadic(
public static final SqlSingleOperandTypeChecker INTEGER =
family(SqlTypeFamily.INTEGER);
+ public static final SqlSingleOperandTypeChecker NUMERIC_OPTIONAL_NUMERIC =
+ family(ImmutableList.of(SqlTypeFamily.NUMERIC, SqlTypeFamily.NUMERIC),
+ // Second operand optional (operand index 0, 1)
+ number -> number == 1);
+
public static final SqlSingleOperandTypeChecker NUMERIC_OPTIONAL_INTEGER =
family(ImmutableList.of(SqlTypeFamily.NUMERIC, SqlTypeFamily.INTEGER),
// Second operand optional (operand index 0, 1)
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 9edc77b636d..dae7fdc0c02 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -432,6 +432,7 @@ public enum BuiltInMethod {
RAND_INTEGER(RandomFunction.class, "randInteger", int.class),
RAND_INTEGER_SEED(RandomFunction.class, "randIntegerSeed", int.class,
int.class),
+ LOG(SqlFunctions.class, "log", long.class, long.class),
TANH(SqlFunctions.class, "tanh", long.class),
SINH(SqlFunctions.class, "sinh", long.class),
TRUNCATE(SqlFunctions.class, "truncate", String.class, int.class),
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index c1a441db80a..f9ea1d9d60c 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -2706,6 +2706,7 @@ BigQuery's type system uses confusingly different names for types and functions:
| b o | LEAST(expr [, expr ]* ) | Returns the least of the expressions
| b m p | LEFT(string, length) | Returns the leftmost *length* characters from the *string*
| b | LENGTH(string) | Equivalent to `CHAR_LENGTH(string)`
+| b | LOG(numeric1 [, numeric2 ]) | Returns the logarithm of *numeric1* to base *numeric2*, or base e if *numeric2* is not present
| b o | LPAD(string, length[, pattern ]) | Returns a string or bytes value that consists of *string* prepended to *length* with *pattern*
| m | TO_BASE64(string) | Converts the *string* to base-64 encoded form and returns a encoded string
| b m | FROM_BASE64(string) | Returns the decoded result of a base-64 *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 8cd4bff3900..a586c264f7d 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -5061,7 +5061,7 @@ private static void checkIf(SqlOperatorFixture f) {
f.checkNull("ln(cast(null as tinyint))");
}
- @Test void testLogFunc() {
+ @Test void testLog10Func() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.LOG10, VmName.EXPAND);
f.checkScalarApprox("log10(10)", "DOUBLE NOT NULL",
@@ -5077,6 +5077,32 @@ private static void checkIf(SqlOperatorFixture f) {
f.checkNull("log10(cast(null as real))");
}
+ @Test void testLogFunc() {
+ final SqlOperatorFixture f0 = fixture()
+ .setFor(SqlLibraryOperators.LOG, VmName.EXPAND);
+ f0.checkFails("^log(100, 10)^",
+ "No match found for function signature LOG\\(