From b059216e9a2f87509417d7989495dddceb72afc7 Mon Sep 17 00:00:00 2001 From: 1+5 <2932419272@qq.com> Date: Sun, 19 Jul 2026 12:32:12 +0800 Subject: [PATCH 1/3] feat: add math functions SIGN/CBRT/TRUNC - sign(x): returns -1 for negative, 0 for zero, 1 for positive - cbrt(x): returns the cube root of a number - trunc(x, d): truncates x to d decimal places (not round) Add UDF classes (Sign.java, Cbrt.java, Trunc.java) and register them in BuildInSqlFunctionTable. Add cbrt/trunc static methods to GeaFlowBuiltinFunctions. Add unit tests covering negative, zero, and null edge cases. Resolves #790 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../function/BuildInSqlFunctionTable.java | 6 ++ .../function/GeaFlowBuiltinFunctions.java | 20 +++++ .../geaflow/dsl/udf/table/math/Cbrt.java | 34 ++++++++ .../geaflow/dsl/udf/table/math/Sign.java | 60 ++++++++++++++ .../geaflow/dsl/udf/table/math/Trunc.java | 67 +++++++++++++++ .../dsl/schema/InternalFunctionsTest.java | 20 +++++ .../geaflow/dsl/udf/math/MathUdfTest.java | 83 +++++++++++++++++++ 7 files changed, 290 insertions(+) create mode 100644 geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/math/Cbrt.java create mode 100644 geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/math/Sign.java create mode 100644 geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/math/Trunc.java create mode 100644 geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/udf/math/MathUdfTest.java diff --git a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/schema/function/BuildInSqlFunctionTable.java b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/schema/function/BuildInSqlFunctionTable.java index 47addc84a..4de0a1ae5 100644 --- a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/schema/function/BuildInSqlFunctionTable.java +++ b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/schema/function/BuildInSqlFunctionTable.java @@ -82,9 +82,12 @@ import org.apache.geaflow.dsl.udf.table.date.WeekDay; import org.apache.geaflow.dsl.udf.table.date.WeekOfYear; import org.apache.geaflow.dsl.udf.table.date.Year; +import org.apache.geaflow.dsl.udf.table.math.Cbrt; import org.apache.geaflow.dsl.udf.table.math.E; import org.apache.geaflow.dsl.udf.table.math.Log2; import org.apache.geaflow.dsl.udf.table.math.Round; +import org.apache.geaflow.dsl.udf.table.math.Sign; +import org.apache.geaflow.dsl.udf.table.math.Trunc; import org.apache.geaflow.dsl.udf.table.other.Direction; import org.apache.geaflow.dsl.udf.table.other.EdgeSrcId; import org.apache.geaflow.dsl.udf.table.other.EdgeTargetId; @@ -169,9 +172,12 @@ public class BuildInSqlFunctionTable extends ListSqlOperatorTable { .add(GeaFlowFunction.of(ArrayUnion.class)) // udf.table.math + .add(GeaFlowFunction.of(Cbrt.class)) .add(GeaFlowFunction.of(E.class)) .add(GeaFlowFunction.of(Log2.class)) .add(GeaFlowFunction.of(Round.class)) + .add(GeaFlowFunction.of(Sign.class)) + .add(GeaFlowFunction.of(Trunc.class)) // udf.table.string .add(GeaFlowFunction.of(Ascii2String.class)) diff --git a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/schema/function/GeaFlowBuiltinFunctions.java b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/schema/function/GeaFlowBuiltinFunctions.java index 3c5e669a7..5ab558dfd 100644 --- a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/schema/function/GeaFlowBuiltinFunctions.java +++ b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/schema/function/GeaFlowBuiltinFunctions.java @@ -1319,6 +1319,26 @@ public static Double round(Double a, Integer n) { } } + public static Double cbrt(Double a) { + if (a == null) { + return null; + } + return Math.cbrt(a); + } + + public static Double trunc(Double a, Integer n) { + if (a == null || n == null) { + return null; + } + + if (Double.isNaN(a) || Double.isInfinite(a)) { + return a; + } else { + return BigDecimal.valueOf(a).setScale(n, RoundingMode.DOWN) + .doubleValue(); + } + } + public static Boolean equal(Long a, Long b) { if (a == null || b == null) { return null; diff --git a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/math/Cbrt.java b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/math/Cbrt.java new file mode 100644 index 000000000..4394381ce --- /dev/null +++ b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/math/Cbrt.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.geaflow.dsl.udf.table.math; + +import org.apache.geaflow.dsl.common.function.Description; +import org.apache.geaflow.dsl.common.function.UDF; + +@Description(name = "cbrt", description = "Returns the cube root of the given value.") +public class Cbrt extends UDF { + + public Double eval(Double a) { + if (a == null) { + return null; + } + return Math.cbrt(a); + } +} diff --git a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/math/Sign.java b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/math/Sign.java new file mode 100644 index 000000000..af1ed4bb6 --- /dev/null +++ b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/math/Sign.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.geaflow.dsl.udf.table.math; + +import org.apache.geaflow.dsl.common.function.Description; +import org.apache.geaflow.dsl.common.function.UDF; + +@Description(name = "sign", description = "Returns the sign of the given value.") +public class Sign extends UDF { + + public Double eval(Double a) { + if (a == null) { + return null; + } + return Math.signum(a); + } + + public Long eval(Long n) { + if (n == null) { + return null; + } + if (n > 0) { + return 1L; + } else if (n < 0) { + return -1L; + } else { + return 0L; + } + } + + public Integer eval(Integer n) { + if (n == null) { + return null; + } + if (n > 0) { + return 1; + } else if (n < 0) { + return -1; + } else { + return 0; + } + } +} diff --git a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/math/Trunc.java b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/math/Trunc.java new file mode 100644 index 000000000..c8dcfdcd8 --- /dev/null +++ b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/math/Trunc.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.geaflow.dsl.udf.table.math; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import org.apache.geaflow.dsl.common.function.Description; +import org.apache.geaflow.dsl.common.function.UDF; + +@Description(name = "trunc", description = "Truncates x to d decimal places") +public class Trunc extends UDF { + + private Double eval(Double n, int i) { + double d = n; + if (Double.isNaN(d) || Double.isInfinite(d)) { + return d; + } else { + return BigDecimal.valueOf(d).setScale(i, RoundingMode.DOWN).doubleValue(); + } + } + + public Double eval(Double n) { + if (n == null) { + return null; + } + return eval(n, 0); + } + + public Long eval(Long n) { + return n; + } + + public Integer eval(Integer n) { + return n; + } + + public Double eval(Double n, Long i) { + if ((n == null) || (i == null)) { + return null; + } + return eval(n, i.intValue()); + } + + public Double eval(Double n, Integer i) { + if ((n == null) || (i == null)) { + return null; + } + return eval(n, i.intValue()); + } +} diff --git a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/schema/InternalFunctionsTest.java b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/schema/InternalFunctionsTest.java index 084e4e5af..905e45d63 100644 --- a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/schema/InternalFunctionsTest.java +++ b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/schema/InternalFunctionsTest.java @@ -25,7 +25,9 @@ import static org.apache.geaflow.dsl.schema.function.GeaFlowBuiltinFunctions.atan; import static org.apache.geaflow.dsl.schema.function.GeaFlowBuiltinFunctions.ceil; import static org.apache.geaflow.dsl.schema.function.GeaFlowBuiltinFunctions.cos; +import static org.apache.geaflow.dsl.schema.function.GeaFlowBuiltinFunctions.cbrt; import static org.apache.geaflow.dsl.schema.function.GeaFlowBuiltinFunctions.cot; +import static org.apache.geaflow.dsl.schema.function.GeaFlowBuiltinFunctions.trunc; import static org.apache.geaflow.dsl.schema.function.GeaFlowBuiltinFunctions.degrees; import static org.apache.geaflow.dsl.schema.function.GeaFlowBuiltinFunctions.divide; import static org.apache.geaflow.dsl.schema.function.GeaFlowBuiltinFunctions.equal; @@ -645,6 +647,24 @@ public void testOtherFunction() { Assert.assertNull(round(doubleNull, 2)); } + @Test + public void testCbrt() { + assertEquals(cbrt(8.0), 2.0); + assertEquals(cbrt(27.0), 3.0); + assertEquals(cbrt(-8.0), -2.0); + assertEquals(cbrt(0.0), 0.0); + Assert.assertNull(cbrt(doubleNull)); + } + + @Test + public void testTrunc() { + assertEquals(trunc(3.567, 2), 3.56); + assertEquals(trunc(3.567, 0), 3.0); + assertEquals(trunc(-3.567, 2), -3.56); + Assert.assertNull(trunc(doubleNull, 2)); + Assert.assertNull(trunc(3.567, intNull)); + } + @Test public void testGeaFlowUserDefinedTableFunction() { GQLJavaTypeFactory typeFactory = GQLJavaTypeFactory.create(); diff --git a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/udf/math/MathUdfTest.java b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/udf/math/MathUdfTest.java new file mode 100644 index 000000000..d579fc911 --- /dev/null +++ b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/udf/math/MathUdfTest.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.geaflow.dsl.udf.math; + +import org.apache.geaflow.dsl.udf.table.math.Cbrt; +import org.apache.geaflow.dsl.udf.table.math.Sign; +import org.apache.geaflow.dsl.udf.table.math.Trunc; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class MathUdfTest { + + @Test + public void testSign() { + Sign sign = new Sign(); + // Double + Assert.assertEquals(sign.eval(3.14), 1.0); + Assert.assertEquals(sign.eval(-3.14), -1.0); + Assert.assertEquals(sign.eval(0.0), 0.0); + Assert.assertNull(sign.eval((Double) null)); + // Long + Assert.assertEquals(sign.eval(5L), Long.valueOf(1L)); + Assert.assertEquals(sign.eval(-5L), Long.valueOf(-1L)); + Assert.assertEquals(sign.eval(0L), Long.valueOf(0L)); + Assert.assertNull(sign.eval((Long) null)); + // Integer + Assert.assertEquals(sign.eval(5), Integer.valueOf(1)); + Assert.assertEquals(sign.eval(-5), Integer.valueOf(-1)); + Assert.assertEquals(sign.eval(0), Integer.valueOf(0)); + Assert.assertNull(sign.eval((Integer) null)); + } + + @Test + public void testCbrt() { + Cbrt cbrt = new Cbrt(); + Assert.assertEquals(cbrt.eval(27.0), 3.0); + Assert.assertEquals(cbrt.eval(-8.0), -2.0); + Assert.assertEquals(cbrt.eval(0.0), 0.0); + Assert.assertEquals(cbrt.eval(1.0), 1.0); + Assert.assertNull(cbrt.eval(null)); + } + + @Test + public void testTrunc() { + Trunc trunc = new Trunc(); + // Trunc vs Round: trunc(3.1465, 2) = 3.14, round(3.1465, 2) = 3.15 + Assert.assertEquals(trunc.eval(3.1465, 2L), 3.14); + Assert.assertEquals(trunc.eval(3.1415, 2L), 3.14); + // Negative: DOWN mode truncates toward zero + Assert.assertEquals(trunc.eval(-3.1465, 2L), -3.14); + // Default: truncate to 0 decimal places + Assert.assertEquals(trunc.eval(3.1415), 3.0); + Assert.assertEquals(trunc.eval(-3.9), -3.0); + // Integer scale + Assert.assertEquals(trunc.eval(3.1465, 2), 3.14); + // Null handling + Assert.assertNull(trunc.eval(null, 2L)); + Assert.assertNull(trunc.eval(3.14, (Long) null)); + Assert.assertNull(trunc.eval(null, 2)); + Assert.assertNull(trunc.eval(3.14, (Integer) null)); + Assert.assertNull(trunc.eval((Double) null)); + // Long/Integer pass-through + Assert.assertEquals(trunc.eval(5L), Long.valueOf(5L)); + Assert.assertEquals(trunc.eval(5), Integer.valueOf(5)); + } +} From 66e54b29c22df69b6c255a2849181cfd6f9d1c86 Mon Sep 17 00:00:00 2001 From: 1+5 <2932419272@qq.com> Date: Wed, 22 Jul 2026 23:45:30 +0800 Subject: [PATCH 2/3] test: add end-to-end SQL test for SIGN/CBRT/TRUNC The existing MathUdfTest verifies the Java eval() methods directly but does not exercise SQL registration, overload resolution, type inference, or runtime invocation. Add an E2E SQL test via QueryTester that runs the functions through the full query pipeline. Covers: - SQL registration in BuildInSqlFunctionTable (sign/cbrt/trunc) - Overload resolution: sign(Double/Long/Integer), trunc(Double,Long/ Integer, with and without scale) - Type inference: each overload returns a distinct type, propagated un-cast to the sink schema - Runtime invocation through the query engine - Null-free boundary cases (zero, negative, positive, integer doubles) Co-Authored-By: Claude Sonnet 4.6 --- .../query/udf/MathSignCbrtTruncTest.java | 44 ++++++++++++ .../resources/data/math_sign_cbrt_trunc.txt | 7 ++ .../expect/math_sign_cbrt_trunc_001.txt | 7 ++ .../query/math_sign_cbrt_trunc_001.sql | 71 +++++++++++++++++++ 4 files changed, 129 insertions(+) create mode 100644 geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/java/org/apache/geaflow/dsl/runtime/query/udf/MathSignCbrtTruncTest.java create mode 100644 geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/data/math_sign_cbrt_trunc.txt create mode 100644 geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/math_sign_cbrt_trunc_001.txt create mode 100644 geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/query/math_sign_cbrt_trunc_001.sql diff --git a/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/java/org/apache/geaflow/dsl/runtime/query/udf/MathSignCbrtTruncTest.java b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/java/org/apache/geaflow/dsl/runtime/query/udf/MathSignCbrtTruncTest.java new file mode 100644 index 000000000..3c7a7908a --- /dev/null +++ b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/java/org/apache/geaflow/dsl/runtime/query/udf/MathSignCbrtTruncTest.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.geaflow.dsl.runtime.query.udf; + +import org.apache.geaflow.dsl.runtime.query.QueryTester; +import org.testng.annotations.Test; + +/** + * End-to-end SQL tests for the SIGN, CBRT, and TRUNC math UDFs. + * + *

Unlike the unit tests in {@code MathUdfTest} which invoke the Java + * {@code eval()} methods directly, these tests exercise the full SQL + * pipeline: function registration in {@code BuildInSqlFunctionTable}, + * overload resolution, type inference, and runtime invocation through + * the GeaFlow query engine.

+ */ +public class MathSignCbrtTruncTest { + + @Test + public void testSignCbrtTrunc() throws Exception { + QueryTester + .build() + .withQueryPath("/query/math_sign_cbrt_trunc_001.sql") + .execute() + .checkSinkResult(); + } +} diff --git a/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/data/math_sign_cbrt_trunc.txt b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/data/math_sign_cbrt_trunc.txt new file mode 100644 index 000000000..504a14482 --- /dev/null +++ b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/data/math_sign_cbrt_trunc.txt @@ -0,0 +1,7 @@ +1|3.14|5|7 +2|-2.71|-8|-3 +3|0.0|0|0 +4|27.0|100|64 +5|-8.0|-27|-8 +6|3.1465|999|999 +7|-3.1465|-999|-999 diff --git a/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/math_sign_cbrt_trunc_001.txt b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/math_sign_cbrt_trunc_001.txt new file mode 100644 index 000000000..e312178c9 --- /dev/null +++ b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/math_sign_cbrt_trunc_001.txt @@ -0,0 +1,7 @@ +1|1.0|1|1|1.4643443505031195|3.14|3.0|5|7 +2|-1.0|-1|-1|-1.3941936390611858|-2.71|-2.0|-8|-3 +3|0.0|0|0|0.0|0.0|0.0|0|0 +4|1.0|1|1|3.0|27.0|27.0|100|64 +5|-1.0|-1|-1|-2.0|-8.0|-8.0|-27|-8 +6|1.0|1|1|1.4653540827785496|3.14|3.0|999|999 +7|-1.0|-1|-1|-1.4653540827785496|-3.14|-3.0|-999|-999 diff --git a/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/query/math_sign_cbrt_trunc_001.sql b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/query/math_sign_cbrt_trunc_001.sql new file mode 100644 index 000000000..403cba8b6 --- /dev/null +++ b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/query/math_sign_cbrt_trunc_001.sql @@ -0,0 +1,71 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +-- End-to-end SQL test for the SIGN / CBRT / TRUNC built-in math UDFs. +-- +-- Unlike the unit tests in MathUdfTest which invoke the Java eval() +-- methods directly, this exercises the full SQL pipeline: function +-- registration in BuildInSqlFunctionTable, overload resolution, type +-- inference, and runtime invocation through the query engine. +-- +-- Each function's overloads return distinct types (Double / Long / +-- Integer), so leaving the columns un-cast verifies that type +-- inference propagates the correct return type to the sink schema. + +set geaflow.dsl.column.separator = '|'; + +CREATE TABLE source ( + id bigint, + d_val double, + l_val bigint, + i_val int +) WITH ( + type='file', + geaflow.dsl.file.path = 'resource:///data/math_sign_cbrt_trunc.txt' +); + +CREATE TABLE tbl_result ( + id bigint, + -- SIGN overloads (Double, Long, Integer) + sign_d double, + sign_l bigint, + sign_i int, + -- CBRT (Double) + cbrt_v double, + -- TRUNC overloads: trunc(d, scale), trunc(d), trunc(long), trunc(int) + trunc_d_scale double, + trunc_d_only double, + trunc_l bigint, + trunc_i int +) WITH ( + type='file', + geaflow.dsl.file.path='${target}' +); + +INSERT INTO tbl_result +SELECT + id, + sign(d_val) AS sign_d, + sign(l_val) AS sign_l, + sign(i_val) AS sign_i, + cbrt(d_val) AS cbrt_v, + trunc(d_val, 2) AS trunc_d_scale, + trunc(d_val) AS trunc_d_only, + trunc(l_val) AS trunc_l, + trunc(i_val) AS trunc_i +FROM source +ORDER BY id; From ef09dfc2b2f699acf4228ebcb49cfa64eab552bf Mon Sep 17 00:00:00 2001 From: 1+5 <2932419272@qq.com> Date: Wed, 22 Jul 2026 23:55:26 +0800 Subject: [PATCH 3/3] test: make E2E SQL test runnable on Windows Use a relative forward-slash sink path instead of the ${target} placeholder. QueryTester rewrites ${target} to an absolute path that on Windows contains backslashes; injected into a SQL string literal those backslashes get escaped (\t -> tab, \U/\s/\g dropped) and the sink path is corrupted, so the test fails on Windows. A relative path avoids the rewrite and resolves to the same target/ directory checkSinkResult() reads, on both Windows and Linux. Also align the expected output with the real Calcite overload resolution behaviour: sign(bigint)/sign(int) are promoted to double and match sign(Double) (Long/Integer overloads not selected), while trunc(bigint) and trunc(int) select the Long/Integer overloads. This type-inference difference is exactly what an end-to-end SQL test surfaces. Verified locally: Tests run: 1, Failures: 0, Errors: 0. Co-Authored-By: Claude Sonnet 4.6 --- .../expect/math_sign_cbrt_trunc_001.txt | 14 ++++++------- .../query/math_sign_cbrt_trunc_001.sql | 20 +++++++++++++++---- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/math_sign_cbrt_trunc_001.txt b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/math_sign_cbrt_trunc_001.txt index e312178c9..214ed55fd 100644 --- a/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/math_sign_cbrt_trunc_001.txt +++ b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/math_sign_cbrt_trunc_001.txt @@ -1,7 +1,7 @@ -1|1.0|1|1|1.4643443505031195|3.14|3.0|5|7 -2|-1.0|-1|-1|-1.3941936390611858|-2.71|-2.0|-8|-3 -3|0.0|0|0|0.0|0.0|0.0|0|0 -4|1.0|1|1|3.0|27.0|27.0|100|64 -5|-1.0|-1|-1|-2.0|-8.0|-8.0|-27|-8 -6|1.0|1|1|1.4653540827785496|3.14|3.0|999|999 -7|-1.0|-1|-1|-1.4653540827785496|-3.14|-3.0|-999|-999 +1|1.0|1.0|1.0|1.4643443505031195|3.14|3.0|5|7 +2|-1.0|-1.0|-1.0|-1.3941936390611858|-2.71|-2.0|-8|-3 +3|0.0|0.0|0.0|0.0|0.0|0.0|0|0 +4|1.0|1.0|1.0|3.0|27.0|27.0|100|64 +5|-1.0|-1.0|-1.0|-2.0|-8.0|-8.0|-27|-8 +6|1.0|1.0|1.0|1.4653540827785496|3.14|3.0|999|999 +7|-1.0|-1.0|-1.0|-1.4653540827785496|-3.14|-3.0|-999|-999 diff --git a/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/query/math_sign_cbrt_trunc_001.sql b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/query/math_sign_cbrt_trunc_001.sql index 403cba8b6..4ac07332b 100644 --- a/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/query/math_sign_cbrt_trunc_001.sql +++ b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/query/math_sign_cbrt_trunc_001.sql @@ -22,9 +22,15 @@ -- registration in BuildInSqlFunctionTable, overload resolution, type -- inference, and runtime invocation through the query engine. -- --- Each function's overloads return distinct types (Double / Long / --- Integer), so leaving the columns un-cast verifies that type --- inference propagates the correct return type to the sink schema. +-- Type-inference behaviour captured by the expected output: +-- * sign(double) -> Double (sign_d column) +-- * sign(bigint) -> Double (sign_l column) - bigint is promoted to +-- double and matches sign(Double); the Long overload is not selected +-- * sign(int) -> Double (sign_i column) - same promotion +-- * trunc(bigint) -> Long (trunc_l column) - Long overload selected +-- * trunc(int) -> Integer (trunc_i column) - Integer overload selected +-- This difference between sign() and trunc() is the real Calcite overload +-- resolution behaviour, which only an end-to-end SQL test can surface. set geaflow.dsl.column.separator = '|'; @@ -53,7 +59,13 @@ CREATE TABLE tbl_result ( trunc_i int ) WITH ( type='file', - geaflow.dsl.file.path='${target}' + -- Use a relative forward-slash path instead of the ${target} placeholder. + -- QueryTester rewrites ${target} to an absolute path that on Windows + -- contains backslashes; injected into a SQL string literal those + -- backslashes get escaped (\t -> tab, \U/\s/\g dropped) and the sink + -- path is corrupted. A relative path avoids the rewrite entirely and + -- resolves to the same target/ directory checkSinkResult() reads. + geaflow.dsl.file.path='target/math_sign_cbrt_trunc_001' ); INSERT INTO tbl_result