Skip to content

Commit

Permalink
[CALCITE-5565] Add LOG function (enabled in BigQuery library)
Browse files Browse the repository at this point in the history
  • Loading branch information
tanclary authored and julianhyde committed Apr 11, 2023
1 parent 2d0b3ac commit 74bc025
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 18 deletions.
62 changes: 62 additions & 0 deletions babel/src/test/resources/sql/big-query.iq
Original file line number Diff line number Diff line change
Expand Up @@ -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
#
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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());

Expand Down Expand Up @@ -3684,6 +3687,41 @@ private static class LogicalNotImplementor extends AbstractRexCallImplementor {
}
}

/** Implementor for the {@code LN}, {@code LOG}, and {@code LOG10} operators.
*
* <p>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 List<Expression> argValueList) {
return Expressions.call(BuiltInMethod.LOG.method, args(call, argValueList));
}

private static List<Expression> args(RexCall call,
List<Expression> argValueList) {
Expression operand0 = argValueList.get(0);
final Expressions.FluentList<Expression> list = Expressions.list(operand0);
switch (call.getOperator().getName()) {
case "LOG":
if (argValueList.size() == 2) {
return list.append(argValueList.get(1));
}
// fall through
case "LN":
return list.append(Expressions.constant(Math.exp(1)));
case "LOG10":
return list.append(Expressions.constant(BigDecimal.TEN));
default:
throw new AssertionError("Operator not found: " + call.getOperator());
}
}
}

/**
* Implementation that calls a given {@link java.lang.reflect.Method}.
*
Expand Down
31 changes: 16 additions & 15 deletions core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
Original file line number Diff line number Diff line change
Expand Up @@ -1567,28 +1567,29 @@ public static double power(BigDecimal b0, BigDecimal b1) {
return Math.pow(b0.doubleValue(), b1.doubleValue());
}

// LN

/** SQL {@code LN(number)} function applied to double values. */
public static double ln(double d) {
return Math.log(d);
}
// LN, LOG, LOG10

/** SQL {@code LN(number)} function applied to BigDecimal values. */
public static double ln(BigDecimal d) {
return Math.log(d.doubleValue());
/** SQL {@code LOG(number, number2)} function applied to double values. */
public static double log(double d0, double d1) {
return Math.log(d0) / Math.log(d1);
}

// LOG10
/** SQL {@code LOG(number, number2)} function applied to
* double and BigDecimal values. */
public static double log(double d0, BigDecimal d1) {
return Math.log(d0) / Math.log(d1.doubleValue());
}

/** SQL <code>LOG10(numeric)</code> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
1 change: 1 addition & 0 deletions site/_docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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\\(<NUMERIC>, <NUMERIC>\\)", false);
final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.BIG_QUERY);
f.checkScalarApprox("log(10, 10)", "DOUBLE NOT NULL",
isWithin(1.0, 0.000001));
f.checkScalarApprox("log(64, 8)", "DOUBLE NOT NULL",
isWithin(2.0, 0.000001));
f.checkScalarApprox("log(27,3)", "DOUBLE NOT NULL",
isWithin(3.0, 0.000001));
f.checkScalarApprox("log(100, 10)", "DOUBLE NOT NULL",
isWithin(2.0, 0.000001));
f.checkScalarApprox("log(10, 100)", "DOUBLE NOT NULL",
isWithin(0.5, 0.000001));
f.checkScalarApprox("log(cast(10e6 as double), 10)", "DOUBLE NOT NULL",
isWithin(7.0, 0.000001));
f.checkScalarApprox("log(cast(10e8 as float), 10)", "DOUBLE NOT NULL",
isWithin(9.0, 0.000001));
f.checkScalarApprox("log(cast(10e-3 as real), 10)", "DOUBLE NOT NULL",
isWithin(-2.0, 0.000001));
f.checkNull("log(cast(null as real), 10)");
f.checkNull("log(10, cast(null as real))");
}

@Test void testRandFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.RAND, VmName.EXPAND);
Expand Down

0 comments on commit 74bc025

Please sign in to comment.