This is an automated email from the ASF dual-hosted git repository.

xuzifu666 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/main by this push:
     new 17f2f973df [CALCITE-6066] Add HYPOT function (enabled in Spark library)
17f2f973df is described below

commit 17f2f973dfda7b7ef935246cccab546f5806e1f9
Author: Zhen Chen <[email protected]>
AuthorDate: Sun Jan 4 17:15:49 2026 +0800

    [CALCITE-6066] Add HYPOT function (enabled in Spark library)
---
 .../calcite/adapter/enumerable/RexImpTable.java    |  2 +
 .../org/apache/calcite/runtime/SqlFunctions.java   | 12 +++++
 .../calcite/sql/fun/SqlLibraryOperators.java       |  9 ++++
 .../org/apache/calcite/util/BuiltInMethod.java     |  1 +
 site/_docs/reference.md                            |  1 +
 .../org/apache/calcite/test/SqlOperatorTest.java   | 54 ++++++++++++++++++++++
 6 files changed, 79 insertions(+)

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 ca8f93feb9..bbe2743ac7 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
@@ -225,6 +225,7 @@
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.FROM_HEX;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.GETBIT;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.HEX;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.HYPOT;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.ILIKE;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.IS_INF;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.IS_NAN;
@@ -880,6 +881,7 @@ void populate1() {
       defineMethod(CSCH, BuiltInMethod.CSCH.method, NullPolicy.STRICT);
       defineMethod(DEGREES, BuiltInMethod.DEGREES.method, NullPolicy.STRICT);
       defineMethod(FACTORIAL, BuiltInMethod.FACTORIAL.method, 
NullPolicy.STRICT);
+      defineMethod(HYPOT, BuiltInMethod.HYPOT.method, NullPolicy.STRICT);
       defineMethod(IS_INF, BuiltInMethod.IS_INF.method, NullPolicy.STRICT);
       defineMethod(IS_NAN, BuiltInMethod.IS_NAN.method, NullPolicy.STRICT);
       defineMethod(POW, BuiltInMethod.POWER.method, NullPolicy.STRICT);
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 dc4c6c2587..52ea3bbef5 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -4319,6 +4319,18 @@ public static double degrees(double b0) {
     return CombinatoricsUtils.factorial(b0);
   }
 
+  /** SQL <code>HYPOT</code> operator applied to double values. */
+  public static double hypot(double a, double b) {
+    return Math.hypot(a, b);
+  }
+
+  /** SQL <code>HYPOT</code> operator applied to general numeric values. */
+  public static double hypot(Object a, Object b) {
+    final Number left = (Number) a;
+    final Number right = (Number) b;
+    return hypot(left.doubleValue(), right.doubleValue());
+  }
+
   /** SQL <code>IS_INF</code> operator applied to BigDecimal values. */
   public static boolean isInf(BigDecimal b0) {
     return Double.isInfinite(b0.doubleValue());
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 f7f7029ae4..83f8787fd5 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
@@ -2562,6 +2562,15 @@ private static RelDataType 
deriveTypeMapFromEntries(SqlOperatorBinding opBinding
           OperandTypes.NUMERIC,
           SqlFunctionCategory.STRING);
 
+  /** The {@code HYPOT(numeric1, numeric2)} function; returns
+   * sqrt(numeric1^2 + numeric2^2) without intermediate overflow or underflow. 
*/
+  @LibraryOperator(libraries = {SPARK, CLICKHOUSE})
+  public static final SqlFunction HYPOT =
+      SqlBasicFunction.create("HYPOT",
+          ReturnTypes.DOUBLE_NULLABLE,
+          OperandTypes.NUMERIC_NUMERIC,
+          SqlFunctionCategory.NUMERIC);
+
   @LibraryOperator(libraries = {BIG_QUERY, MYSQL, POSTGRESQL, SPARK, HIVE})
   public static final SqlFunction MD5 =
       SqlBasicFunction.create("MD5",
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 3834409640..cfeb4fe77c 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -598,6 +598,7 @@ public enum BuiltInMethod {
   TAND(SqlFunctions.class, "tand", double.class),
   TANH(SqlFunctions.class, "tanh", long.class),
   SINH(SqlFunctions.class, "sinh", long.class),
+  HYPOT(SqlFunctions.class, "hypot", double.class, double.class),
   TRUNCATE(SqlFunctions.class, "truncate", String.class, int.class),
   TRUNCATE_OR_PAD(SqlFunctions.class, "truncateOrPad", String.class, 
int.class),
   TRIM(SqlFunctions.class, "trim", boolean.class, boolean.class, String.class,
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index 643cb69f18..b922253fe0 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -2936,6 +2936,7 @@ ### Dialect-specific Operators
 | b | FORMAT_TIME(string, time)                      | Formats *time* 
according to the specified format *string*
 | b | FORMAT_TIMESTAMP(string timestamp)             | Formats *timestamp* 
according to the specified format *string*
 | s | GETBIT(value, position)                        | Equivalent to 
`BIT_GET(value, position)`
+| s i | HYPOT(numeric1, numeric2)                    | Returns 
sqrt(*numeric1*^2 + *numeric2*^2) without intermediate overflow or underflow
 | b o p r s h | GREATEST(expr [, expr ]*)            | Returns the greatest of 
the expressions
 | b h s | IF(condition, value1, value2)              | Returns *value1* if 
*condition* is TRUE, *value2* otherwise
 | b s | IFNULL(value1, value2)                       | Equivalent to 
`NVL(value1, value2)`
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 90839a1daa..b27c188620 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -10018,6 +10018,60 @@ void checkArrayReverseFunc(SqlOperatorFixture f0, 
SqlFunction function,
     f0.forEachLibrary(list(SqlLibrary.BIG_QUERY, SqlLibrary.SPARK), consumer);
   }
 
+  /** Test case for <a 
href="https://issues.apache.org/jira/browse/CALCITE-6066";>
+   * [CALCITE-6066] Add HYPOT function (enabled in Spark library)</a>. */
+  @Test void testHypotFunc() {
+    final SqlOperatorFixture f0 = fixture().setFor(SqlLibraryOperators.HYPOT);
+    f0.checkFails("^hypot(3, 4)^",
+        "No match found for function signature HYPOT\\(<NUMERIC>, 
<NUMERIC>\\)",
+        false);
+    final Consumer<SqlOperatorFixture> consumer = f -> {
+      f.checkScalarApprox("hypot(3, 4)", "DOUBLE NOT NULL",
+          isWithin(5.0000d, 0.0001d));
+      f.checkScalarApprox("hypot(3.0, cast(4 as bigint))", "DOUBLE NOT NULL",
+          isWithin(5.0000d, 0.0001d));
+      f.checkScalarApprox("hypot(cast(-2 as bigint), cast(-4 as bigint))",
+          "DOUBLE NOT NULL",
+          isWithin(4.4721d, 0.0001d));
+      f.checkScalarApprox("hypot(cast(3.0 as double), cast(4.0 as double))",
+          "DOUBLE NOT NULL",
+          isWithin(5.0000d, 0.0001d));
+      f.checkScalarApprox("hypot(-2.5, cast(-4.5 as double))", "DOUBLE NOT 
NULL",
+          isWithin(5.1478d, 0.0001d));
+      f.checkScalarApprox("hypot(-2.5, -4.5)", "DOUBLE NOT NULL",
+          isWithin(5.1478d, 0.0001d));
+      f.checkScalarApprox("hypot(cast(3 as float), cast(4 as real))", "DOUBLE 
NOT NULL",
+          isWithin(5.0000d, 0.0001d));
+      f.checkType("hypot(cast(null as bigint), 1)", "DOUBLE");
+      f.checkNull("hypot(cast(null as bigint), 1)");
+      f.checkNull("hypot(1, cast(null as bigint))");
+      f.checkNull("hypot(cast(null as bigint), cast(null as bigint))");
+      f.checkNull("hypot(cast(null as double), cast(null as double))");
+      f.checkNull("hypot(cast(null as decimal), cast(null as decimal))");
+
+      // unsigned type
+      f.checkScalarApprox("hypot(cast(3 as integer unsigned), cast(4 as 
integer unsigned))",
+          "DOUBLE NOT NULL", isWithin(5.0000d, 0.0001d));
+      f.checkScalarApprox("hypot(cast(3 as bigint unsigned), cast(4 as integer 
unsigned))",
+          "DOUBLE NOT NULL", isWithin(5.0000d, 0.0001d));
+      f.checkScalarApprox("hypot(cast(3 as bigint unsigned), cast(4 as bigint 
unsigned))",
+          "DOUBLE NOT NULL", isWithin(5.0000d, 0.0001d));
+      f.checkScalarApprox("hypot(cast(3 as smallint unsigned), cast(4 as 
smallint unsigned))",
+          "DOUBLE NOT NULL", isWithin(5.0000d, 0.0001d));
+      f.checkScalarApprox("hypot(cast(3 as tinyint unsigned), cast(4 as 
tinyint unsigned))",
+          "DOUBLE NOT NULL", isWithin(5.0000d, 0.0001d));
+
+      // mixed type
+      f.checkScalarApprox("hypot(cast(3 as tinyint), cast(4 as tinyint 
unsigned))",
+          "DOUBLE NOT NULL", isWithin(5.0000d, 0.0001d));
+      f.checkScalarApprox("hypot(cast(3 as bigint), cast(4 as tinyint 
unsigned))",
+          "DOUBLE NOT NULL", isWithin(5.0000d, 0.0001d));
+      f.checkScalarApprox("hypot(cast(3 as double), cast(4 as tinyint 
unsigned))",
+          "DOUBLE NOT NULL", isWithin(5.0000d, 0.0001d));
+    };
+    f0.forEachLibrary(list(SqlLibrary.SPARK, SqlLibrary.CLICKHOUSE), consumer);
+  }
+
   @Test void testInfinity() {
     final SqlOperatorFixture f = fixture();
     f.checkScalar("cast('Infinity' as double)", "Infinity",

Reply via email to