Skip to content

Commit

Permalink
[CALCITE-6325] Add LOG function (enabled in Mysql, Spark library)
Browse files Browse the repository at this point in the history
  • Loading branch information
caicancai committed Mar 13, 2024
1 parent c546d4a commit d49c994
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@
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;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.LOG_MS;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.LPAD;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.MAP;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.MAP_CONCAT;
Expand Down Expand Up @@ -638,12 +639,14 @@ Builder populate() {
defineMethod(EXP, BuiltInMethod.EXP.method, NullPolicy.STRICT);
defineMethod(POWER, BuiltInMethod.POWER.method, NullPolicy.STRICT);
defineMethod(ABS, BuiltInMethod.ABS.method, NullPolicy.STRICT);
defineMethod(LOG2, BuiltInMethod.LOG2.method, NullPolicy.STRICT);

map.put(LN, new LogImplementor());
map.put(LOG, new LogImplementor());
map.put(LOG10, new LogImplementor());

map.put(LOG_MS, new LogMSImplementor());
map.put(LOG2, new LogMSImplementor());

defineReflective(RAND, BuiltInMethod.RAND.method,
BuiltInMethod.RAND_SEED.method);
defineReflective(RAND_INTEGER, BuiltInMethod.RAND_INTEGER.method,
Expand Down Expand Up @@ -4136,9 +4139,52 @@ private static List<Expression> args(RexCall call,
if (argValueList.size() == 2) {
return list.append(argValueList.get(1));
}
if (argValueList.size() == 1) {
return list.append(Expressions.constant(Math.exp(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());
}
}
}

/** 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 LogMSImplementor extends AbstractRexCallImplementor {
LogMSImplementor() {
super("logMS", NullPolicy.STRICT, true);
}

@Override Expression implementSafe(final RexToLixTranslator translator,
final RexCall call, final List<Expression> argValueList) {
return Expressions.call(BuiltInMethod.LOGMS.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));
}
if (argValueList.size() == 1) {
return list.append(Expressions.constant(Math.exp(1)));
}
// fall through
case "LN":
return list.append(Expressions.constant(Math.exp(1)));
case "LOG2":
return list.append(Expressions.constant(2));
case "LOG10":
return list.append(Expressions.constant(BigDecimal.TEN));
default:
Expand Down
25 changes: 18 additions & 7 deletions core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
Original file line number Diff line number Diff line change
Expand Up @@ -2788,15 +2788,26 @@ public static double log(BigDecimal d0, BigDecimal d1) {
return Math.log(d0.doubleValue()) / Math.log(d1.doubleValue());
}

/** SQL {@code LOG2(number)} function applied to double values. */
public static @Nullable Double log2(double number) {
return (number <= 0) ? null : log(number, 2);
/** SQL {@code LOG(number, number2)} function applied to double values. */
public static @Nullable Double logMS(double number, double number2) {
return (number <= 0) ? null : log(number, number2);
}

/** SQL {@code LOGMS(number, number2)} function applied to
* double and BigDecimal values. */
public static @Nullable Double logMS(double number, BigDecimal number2) {
return (number <= 0) ? null : log(number, number2);
}

/** SQL {@code LOGMS(number, number2)} function applied to
* BigDecimal and double values. */
public static @Nullable Double logMS(BigDecimal number, double number2) {
return (number.doubleValue() <= 0) ? null : log(number, number2);
}

/** SQL {@code LOG2(number)} function applied to
* BigDecimal values. */
public static @Nullable Double log2(BigDecimal number) {
return log2(number.doubleValue());
/** SQL {@code LOGMS(number, number2)} function applied to double values. */
public static @Nullable Double logMS(BigDecimal number, BigDecimal number2) {
return (number.doubleValue() <= 0) ? null : log(number, number2);
}

// MOD
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2149,6 +2149,14 @@ private static RelDataType deriveTypeMapFromEntries(SqlOperatorBinding opBinding
OperandTypes.NUMERIC_OPTIONAL_NUMERIC,
SqlFunctionCategory.NUMERIC);

/** The "LOG(numeric, numeric1)" function. Returns the base numeric1 logarithm of numeric. */
@LibraryOperator(libraries = {MYSQL, SPARK})
public static final SqlFunction LOG_MS =
SqlBasicFunction.create("LOG",
ReturnTypes.DOUBLE_FORCE_NULLABLE,
OperandTypes.NUMERIC_OPTIONAL_NUMERIC,
SqlFunctionCategory.NUMERIC);

/** The "LOG2(numeric)" function. Returns the base 2 logarithm of numeric. */
@LibraryOperator(libraries = {MYSQL, SPARK})
public static final SqlFunction LOG2 =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,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),
LOG2(SqlFunctions.class, "log2", long.class),
LOGMS(SqlFunctions.class, "logMS", long.class, long.class),
SEC(SqlFunctions.class, "sec", double.class),
SECH(SqlFunctions.class, "sech", double.class),
SIGN(SqlFunctions.class, "sign", long.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 @@ -2783,6 +2783,7 @@ In the following:
| b f s | LENGTH(string) | Equivalent to `CHAR_LENGTH(string)`
| h s | LEVENSHTEIN(string1, string2) | Returns the Levenshtein distance between *string1* and *string2*
| b | LOG(numeric1 [, numeric2 ]) | Returns the logarithm of *numeric1* to base *numeric2*, or base e if *numeric2* is not present
| m s | LOG(numeric1 [, numeric2 ]) | Returns the logarithm of *numeric1* to base *numeric2*, or base e if *numeric2* is not present
| m s | LOG2(numeric) | Returns the base 2 logarithm of *numeric*
| b o 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
Expand Down
74 changes: 74 additions & 0 deletions testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -6243,6 +6243,20 @@ void checkRegexpExtract(SqlOperatorFixture f0, FunctionAlias functionAlias) {
f.checkNull("log(10, cast(null as real))");
}

@Test void testLogFuncByOneParameter() {
final SqlOperatorFixture f0 = fixture()
.setFor(SqlLibraryOperators.LOG, VmName.EXPAND);
final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.BIG_QUERY);
f0.checkFails("^log(100)^",
"No match found for function signature LOG\\(<NUMERIC>\\)", false);
f.checkScalarApprox("log(2.71828)", "DOUBLE NOT NULL",
isWithin(1.0, 0.000001));
f.checkScalarApprox("log(2.71828)", "DOUBLE NOT NULL",
isWithin(0.999999327, 0.0000001));
f.checkNull("log(cast(null as real), 10)");
f.checkNull("log(10, cast(null as real))");
}

/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-6224">[CALCITE-6224]
* Add LOG2 function (enabled in MYSQL, Spark library)</a>. */
Expand Down Expand Up @@ -6280,6 +6294,66 @@ void checkRegexpExtract(SqlOperatorFixture f0, FunctionAlias functionAlias) {
f0.forEachLibrary(list(SqlLibrary.MYSQL, SqlLibrary.SPARK), consumer);
}

/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-6259">[CALCITE-6259]
* Add LOG function (enabled in MYSQL, Spark library)</a>. */
@Test void testLogMSFunc() {
final SqlOperatorFixture f0 = Fixtures.forOperators(true);
f0.checkFails("^log(100, 10)^",
"No match found for function signature LOG\\(<NUMERIC>, <NUMERIC>\\)", false);
f0.setFor(SqlLibraryOperators.LOG_MS);
final Consumer<SqlOperatorFixture> consumer = f -> {
f.checkScalarApprox("log(10, 10)", "DOUBLE",
isWithin(1.0, 0.000001));
f.checkScalarApprox("log(64, 8)", "DOUBLE",
isWithin(2.0, 0.000001));
f.checkScalarApprox("log(27,3)", "DOUBLE",
isWithin(3.0, 0.000001));
f.checkScalarApprox("log(100, 10)", "DOUBLE",
isWithin(2.0, 0.000001));
f.checkScalarApprox("log(10, 100)", "DOUBLE",
isWithin(0.5, 0.000001));
f.checkScalarApprox("log(cast(10e6 as double), 10)", "DOUBLE",
isWithin(7.0, 0.000001));
f.checkScalarApprox("log(cast(10e8 as float), 10)", "DOUBLE",
isWithin(9.0, 0.000001));
f.checkScalarApprox("log(cast(10e-3 as real), 10)", "DOUBLE",
isWithin(-2.0, 0.000001));
f.checkNull("log(cast(null as real), 10)");
f.checkNull("log(10, cast(null as real))");
f.checkNull("log(0, 2)");
f.checkNull("log(0,-2)");
f.checkNull("log(0, +0.0)");
f.checkNull("log(0, 0.0)");
f.checkNull("log(null)");
f.checkNull("log(cast(null as real))");
};
f0.forEachLibrary(list(SqlLibrary.MYSQL, SqlLibrary.SPARK), consumer);
}

/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-6259">[CALCITE-6259]
* Add LOG function (enabled in MYSQL, Spark library)</a>. */
@Test void testLogMSFuncByOneparameter() {
final SqlOperatorFixture f0 = fixture();
f0.checkFails("^log(100)^",
"No match found for function signature LOG\\(<NUMERIC>\\)", false);
f0.setFor(SqlLibraryOperators.LOG_MS);
final Consumer<SqlOperatorFixture> consumer = f -> {
f.checkScalarApprox("log(2.71828)", "DOUBLE",
isWithin(1.0, 0.000001));
f.checkScalarApprox("log(2.71828)", "DOUBLE",
isWithin(0.999999327, 0.0000001));
f.checkNull("log(cast(null as real))");
f.checkNull("log(cast(null as real))");
f.checkNull("log(0)");
f.checkNull("log(+0.0)");
f.checkNull("log(null)");
f.checkNull("log(cast(null as real))");
};
f0.forEachLibrary(list(SqlLibrary.MYSQL, SqlLibrary.SPARK), consumer);
}

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

0 comments on commit d49c994

Please sign in to comment.