Skip to content

Commit 4c7d3ba

Browse files
committed
[CALCITE-6066] Add HYPOT function (enabled in Spark library)
1 parent 3d14332 commit 4c7d3ba

File tree

6 files changed

+80
-0
lines changed

6 files changed

+80
-0
lines changed

core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@
225225
import static org.apache.calcite.sql.fun.SqlLibraryOperators.FROM_HEX;
226226
import static org.apache.calcite.sql.fun.SqlLibraryOperators.GETBIT;
227227
import static org.apache.calcite.sql.fun.SqlLibraryOperators.HEX;
228+
import static org.apache.calcite.sql.fun.SqlLibraryOperators.HYPOT;
228229
import static org.apache.calcite.sql.fun.SqlLibraryOperators.ILIKE;
229230
import static org.apache.calcite.sql.fun.SqlLibraryOperators.IS_INF;
230231
import static org.apache.calcite.sql.fun.SqlLibraryOperators.IS_NAN;
@@ -880,6 +881,7 @@ void populate1() {
880881
defineMethod(CSCH, BuiltInMethod.CSCH.method, NullPolicy.STRICT);
881882
defineMethod(DEGREES, BuiltInMethod.DEGREES.method, NullPolicy.STRICT);
882883
defineMethod(FACTORIAL, BuiltInMethod.FACTORIAL.method, NullPolicy.STRICT);
884+
defineMethod(HYPOT, BuiltInMethod.HYPOT.method, NullPolicy.STRICT);
883885
defineMethod(IS_INF, BuiltInMethod.IS_INF.method, NullPolicy.STRICT);
884886
defineMethod(IS_NAN, BuiltInMethod.IS_NAN.method, NullPolicy.STRICT);
885887
defineMethod(POW, BuiltInMethod.POWER.method, NullPolicy.STRICT);

core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4319,6 +4319,18 @@ public static double degrees(double b0) {
43194319
return CombinatoricsUtils.factorial(b0);
43204320
}
43214321

4322+
/** SQL <code>HYPOT</code> operator applied to double values. */
4323+
public static double hypot(double a, double b) {
4324+
return Math.hypot(a, b);
4325+
}
4326+
4327+
/** SQL <code>HYPOT</code> operator applied to general numeric values. */
4328+
public static double hypot(Object a, Object b) {
4329+
final Number left = (Number) a;
4330+
final Number right = (Number) b;
4331+
return hypot(left.doubleValue(), right.doubleValue());
4332+
}
4333+
43224334
/** SQL <code>IS_INF</code> operator applied to BigDecimal values. */
43234335
public static boolean isInf(BigDecimal b0) {
43244336
return Double.isInfinite(b0.doubleValue());

core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
import static org.apache.calcite.sql.fun.SqlLibrary.ALL;
6565
import static org.apache.calcite.sql.fun.SqlLibrary.BIG_QUERY;
6666
import static org.apache.calcite.sql.fun.SqlLibrary.CALCITE;
67+
import static org.apache.calcite.sql.fun.SqlLibrary.CLICKHOUSE;
6768
import static org.apache.calcite.sql.fun.SqlLibrary.HIVE;
6869
import static org.apache.calcite.sql.fun.SqlLibrary.MSSQL;
6970
import static org.apache.calcite.sql.fun.SqlLibrary.MYSQL;
@@ -2561,6 +2562,15 @@ private static RelDataType deriveTypeMapFromEntries(SqlOperatorBinding opBinding
25612562
OperandTypes.NUMERIC,
25622563
SqlFunctionCategory.STRING);
25632564

2565+
/** The {@code HYPOT(numeric1, numeric2)} function; returns
2566+
* sqrt(numeric1^2 + numeric2^2) without intermediate overflow or underflow. */
2567+
@LibraryOperator(libraries = {SPARK, CLICKHOUSE})
2568+
public static final SqlFunction HYPOT =
2569+
SqlBasicFunction.create("HYPOT",
2570+
ReturnTypes.DOUBLE_NULLABLE,
2571+
OperandTypes.NUMERIC_NUMERIC,
2572+
SqlFunctionCategory.NUMERIC);
2573+
25642574
@LibraryOperator(libraries = {BIG_QUERY, MYSQL, POSTGRESQL, SPARK, HIVE})
25652575
public static final SqlFunction MD5 =
25662576
SqlBasicFunction.create("MD5",

core/src/main/java/org/apache/calcite/util/BuiltInMethod.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,7 @@ public enum BuiltInMethod {
598598
TAND(SqlFunctions.class, "tand", double.class),
599599
TANH(SqlFunctions.class, "tanh", long.class),
600600
SINH(SqlFunctions.class, "sinh", long.class),
601+
HYPOT(SqlFunctions.class, "hypot", double.class, double.class),
601602
TRUNCATE(SqlFunctions.class, "truncate", String.class, int.class),
602603
TRUNCATE_OR_PAD(SqlFunctions.class, "truncateOrPad", String.class, int.class),
603604
TRIM(SqlFunctions.class, "trim", boolean.class, boolean.class, String.class,

site/_docs/reference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2936,6 +2936,7 @@ In the following:
29362936
| b | FORMAT_TIME(string, time) | Formats *time* according to the specified format *string*
29372937
| b | FORMAT_TIMESTAMP(string timestamp) | Formats *timestamp* according to the specified format *string*
29382938
| s | GETBIT(value, position) | Equivalent to `BIT_GET(value, position)`
2939+
| s i | HYPOT(numeric1, numeric2) | Returns sqrt(*numeric1*^2 + *numeric2*^2) without intermediate overflow or underflow
29392940
| b o p r s h | GREATEST(expr [, expr ]*) | Returns the greatest of the expressions
29402941
| b h s | IF(condition, value1, value2) | Returns *value1* if *condition* is TRUE, *value2* otherwise
29412942
| b s | IFNULL(value1, value2) | Equivalent to `NVL(value1, value2)`

testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10018,6 +10018,60 @@ void checkArrayReverseFunc(SqlOperatorFixture f0, SqlFunction function,
1001810018
f0.forEachLibrary(list(SqlLibrary.BIG_QUERY, SqlLibrary.SPARK), consumer);
1001910019
}
1002010020

10021+
/** Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-6066">
10022+
* [CALCITE-6066] Add HYPOT function (enabled in Spark library)</a>. */
10023+
@Test void testHypotFunc() {
10024+
final SqlOperatorFixture f0 = fixture().setFor(SqlLibraryOperators.HYPOT);
10025+
f0.checkFails("^hypot(3, 4)^",
10026+
"No match found for function signature HYPOT\\(<NUMERIC>, <NUMERIC>\\)",
10027+
false);
10028+
final Consumer<SqlOperatorFixture> consumer = f -> {
10029+
f.checkScalarApprox("hypot(3, 4)", "DOUBLE NOT NULL",
10030+
isWithin(5.0000d, 0.0001d));
10031+
f.checkScalarApprox("hypot(3.0, cast(4 as bigint))", "DOUBLE NOT NULL",
10032+
isWithin(5.0000d, 0.0001d));
10033+
f.checkScalarApprox("hypot(cast(-2 as bigint), cast(-4 as bigint))",
10034+
"DOUBLE NOT NULL",
10035+
isWithin(4.4721d, 0.0001d));
10036+
f.checkScalarApprox("hypot(cast(3.0 as double), cast(4.0 as double))",
10037+
"DOUBLE NOT NULL",
10038+
isWithin(5.0000d, 0.0001d));
10039+
f.checkScalarApprox("hypot(-2.5, cast(-4.5 as double))", "DOUBLE NOT NULL",
10040+
isWithin(5.1478d, 0.0001d));
10041+
f.checkScalarApprox("hypot(-2.5, -4.5)", "DOUBLE NOT NULL",
10042+
isWithin(5.1478d, 0.0001d));
10043+
f.checkScalarApprox("hypot(cast(3 as float), cast(4 as real))", "DOUBLE NOT NULL",
10044+
isWithin(5.0000d, 0.0001d));
10045+
f.checkType("hypot(cast(null as bigint), 1)", "DOUBLE");
10046+
f.checkNull("hypot(cast(null as bigint), 1)");
10047+
f.checkNull("hypot(1, cast(null as bigint))");
10048+
f.checkNull("hypot(cast(null as bigint), cast(null as bigint))");
10049+
f.checkNull("hypot(cast(null as double), cast(null as double))");
10050+
f.checkNull("hypot(cast(null as decimal), cast(null as decimal))");
10051+
10052+
// unsigned type
10053+
f.checkScalarApprox("hypot(cast(3 as integer unsigned), cast(4 as integer unsigned))",
10054+
"DOUBLE NOT NULL", isWithin(5.0000d, 0.0001d));
10055+
f.checkScalarApprox("hypot(cast(3 as bigint unsigned), cast(4 as integer unsigned))",
10056+
"DOUBLE NOT NULL", isWithin(5.0000d, 0.0001d));
10057+
f.checkScalarApprox("hypot(cast(3 as bigint unsigned), cast(4 as bigint unsigned))",
10058+
"DOUBLE NOT NULL", isWithin(5.0000d, 0.0001d));
10059+
f.checkScalarApprox("hypot(cast(3 as smallint unsigned), cast(4 as smallint unsigned))",
10060+
"DOUBLE NOT NULL", isWithin(5.0000d, 0.0001d));
10061+
f.checkScalarApprox("hypot(cast(3 as tinyint unsigned), cast(4 as tinyint unsigned))",
10062+
"DOUBLE NOT NULL", isWithin(5.0000d, 0.0001d));
10063+
10064+
// mixed type
10065+
f.checkScalarApprox("hypot(cast(3 as tinyint), cast(4 as tinyint unsigned))",
10066+
"DOUBLE NOT NULL", isWithin(5.0000d, 0.0001d));
10067+
f.checkScalarApprox("hypot(cast(3 as bigint), cast(4 as tinyint unsigned))",
10068+
"DOUBLE NOT NULL", isWithin(5.0000d, 0.0001d));
10069+
f.checkScalarApprox("hypot(cast(3 as double), cast(4 as tinyint unsigned))",
10070+
"DOUBLE NOT NULL", isWithin(5.0000d, 0.0001d));
10071+
};
10072+
f0.forEachLibrary(list(SqlLibrary.SPARK, SqlLibrary.CLICKHOUSE), consumer);
10073+
}
10074+
1002110075
@Test void testInfinity() {
1002210076
final SqlOperatorFixture f = fixture();
1002310077
f.checkScalar("cast('Infinity' as double)", "Infinity",

0 commit comments

Comments
 (0)