From 619cd871affa00b4f451b3b96a8d9da854a2e10c Mon Sep 17 00:00:00 2001 From: zzq <914700349@qq.com> Date: Mon, 3 Aug 2026 16:38:00 +0800 Subject: [PATCH 1/2] feat: add trim lpad and rpad string functions --- .../function/BuildInSqlFunctionTable.java | 6 ++ .../geaflow/dsl/udf/table/string/LPad.java | 41 ++++++++++ .../geaflow/dsl/udf/table/string/RPad.java | 41 ++++++++++ .../dsl/udf/table/string/StringPadUtil.java | 64 +++++++++++++++ .../geaflow/dsl/udf/table/string/Trim.java | 41 ++++++++++ .../geaflow/dsl/udf/string/PadTest.java | 78 +++++++++++++++++++ .../geaflow/dsl/udf/string/TrimTest.java | 14 ++++ .../dsl/runtime/query/StringFunctionTest.java | 52 +++++++++++++ .../test/resources/expect/function_lpad.txt | 1 + .../test/resources/expect/function_rpad.txt | 1 + .../test/resources/expect/function_trim.txt | 1 + .../test/resources/query/function_lpad.sql | 43 ++++++++++ .../test/resources/query/function_rpad.sql | 43 ++++++++++ .../test/resources/query/function_trim.sql | 37 +++++++++ 14 files changed, 463 insertions(+) create mode 100644 geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/LPad.java create mode 100644 geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/RPad.java create mode 100644 geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/StringPadUtil.java create mode 100644 geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/Trim.java create mode 100644 geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/udf/string/PadTest.java create mode 100644 geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/java/org/apache/geaflow/dsl/runtime/query/StringFunctionTest.java create mode 100644 geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/function_lpad.txt create mode 100644 geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/function_rpad.txt create mode 100644 geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/function_trim.txt create mode 100644 geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/query/function_lpad.sql create mode 100644 geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/query/function_rpad.sql create mode 100644 geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/query/function_trim.sql 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 9656d9299..0d55790b4 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 @@ -114,9 +114,11 @@ import org.apache.geaflow.dsl.udf.table.string.Instr; import org.apache.geaflow.dsl.udf.table.string.IsBlank; import org.apache.geaflow.dsl.udf.table.string.KeyValue; +import org.apache.geaflow.dsl.udf.table.string.LPad; import org.apache.geaflow.dsl.udf.table.string.LTrim; import org.apache.geaflow.dsl.udf.table.string.Length; import org.apache.geaflow.dsl.udf.table.string.Like; +import org.apache.geaflow.dsl.udf.table.string.RPad; import org.apache.geaflow.dsl.udf.table.string.RTrim; import org.apache.geaflow.dsl.udf.table.string.RegExp; import org.apache.geaflow.dsl.udf.table.string.RegExpExtract; @@ -128,6 +130,7 @@ import org.apache.geaflow.dsl.udf.table.string.Space; import org.apache.geaflow.dsl.udf.table.string.SplitEx; import org.apache.geaflow.dsl.udf.table.string.Substr; +import org.apache.geaflow.dsl.udf.table.string.Trim; import org.apache.geaflow.dsl.udf.table.string.UrlDecode; import org.apache.geaflow.dsl.udf.table.string.UrlEncode; import org.apache.geaflow.dsl.util.FunctionUtil; @@ -196,6 +199,7 @@ public class BuildInSqlFunctionTable extends ListSqlOperatorTable { .add(GeaFlowFunction.of(KeyValue.class)) .add(GeaFlowFunction.of(Length.class)) .add(GeaFlowFunction.of(Like.class)) + .add(GeaFlowFunction.of(LPad.class)) .add(GeaFlowFunction.of(LTrim.class)) .add(GeaFlowFunction.of(RegExp.class)) .add(GeaFlowFunction.of(RegexpCount.class)) @@ -204,10 +208,12 @@ public class BuildInSqlFunctionTable extends ListSqlOperatorTable { .add(GeaFlowFunction.of(Repeat.class)) .add(GeaFlowFunction.of(Replace.class)) .add(GeaFlowFunction.of(Reverse.class)) + .add(GeaFlowFunction.of(RPad.class)) .add(GeaFlowFunction.of(RTrim.class)) .add(GeaFlowFunction.of(Space.class)) .add(GeaFlowFunction.of(SplitEx.class)) .add(GeaFlowFunction.of(Substr.class)) + .add(GeaFlowFunction.of(Trim.class)) .add(GeaFlowFunction.of(UrlDecode.class)) .add(GeaFlowFunction.of(UrlEncode.class)) .add(GeaFlowFunction.of(GetJsonObject.class)) diff --git a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/LPad.java b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/LPad.java new file mode 100644 index 000000000..24b01fcd0 --- /dev/null +++ b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/LPad.java @@ -0,0 +1,41 @@ +/* + * 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.string; + +import org.apache.geaflow.common.binary.BinaryString; +import org.apache.geaflow.dsl.common.function.Description; +import org.apache.geaflow.dsl.common.function.UDF; + +@Description(name = "lpad", description = "Returns the string left-padded to the given length " + + "with the specified pad string.") +public class LPad extends UDF { + + public String eval(String str, Integer length, String pad) { + return StringPadUtil.pad(str, length, pad, true); + } + + public BinaryString eval(BinaryString str, Integer length, BinaryString pad) { + if (str == null || length == null || pad == null) { + return null; + } + return BinaryString.fromString(StringPadUtil.pad( + str.toString(), length, pad.toString(), true)); + } +} diff --git a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/RPad.java b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/RPad.java new file mode 100644 index 000000000..93648570d --- /dev/null +++ b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/RPad.java @@ -0,0 +1,41 @@ +/* + * 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.string; + +import org.apache.geaflow.common.binary.BinaryString; +import org.apache.geaflow.dsl.common.function.Description; +import org.apache.geaflow.dsl.common.function.UDF; + +@Description(name = "rpad", description = "Returns the string right-padded to the given length " + + "with the specified pad string.") +public class RPad extends UDF { + + public String eval(String str, Integer length, String pad) { + return StringPadUtil.pad(str, length, pad, false); + } + + public BinaryString eval(BinaryString str, Integer length, BinaryString pad) { + if (str == null || length == null || pad == null) { + return null; + } + return BinaryString.fromString(StringPadUtil.pad( + str.toString(), length, pad.toString(), false)); + } +} diff --git a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/StringPadUtil.java b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/StringPadUtil.java new file mode 100644 index 000000000..6b1a67cf8 --- /dev/null +++ b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/StringPadUtil.java @@ -0,0 +1,64 @@ +/* + * 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.string; + +final class StringPadUtil { + + private StringPadUtil() { + } + + static String pad(String str, Integer length, String pad, boolean left) { + if (str == null || length == null || pad == null) { + return null; + } + if (length <= 0) { + return ""; + } + + int strLength = str.codePointCount(0, str.length()); + if (length <= strLength) { + return substring(str, length); + } + if (pad.isEmpty()) { + return str; + } + + String padding = repeat(pad, length - strLength); + return left ? padding + str : str + padding; + } + + private static String repeat(String pad, int length) { + int padLength = pad.codePointCount(0, pad.length()); + int repeatCount = length / padLength; + int remainder = length % padLength; + StringBuilder result = new StringBuilder(); + for (int i = 0; i < repeatCount; i++) { + result.append(pad); + } + if (remainder > 0) { + result.append(substring(pad, remainder)); + } + return result.toString(); + } + + private static String substring(String value, int codePointCount) { + return value.substring(0, value.offsetByCodePoints(0, codePointCount)); + } +} diff --git a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/Trim.java b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/Trim.java new file mode 100644 index 000000000..90c63e45e --- /dev/null +++ b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/Trim.java @@ -0,0 +1,41 @@ +/* + * 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.string; + +import org.apache.commons.lang3.StringUtils; +import org.apache.geaflow.common.binary.BinaryString; +import org.apache.geaflow.dsl.common.function.Description; +import org.apache.geaflow.dsl.common.function.UDF; + +@Description(name = "trim", description = "Returns a string with leading and trailing " + + "spaces removed.") +public class Trim extends UDF { + + public String eval(String str) { + return StringUtils.strip(str, " "); + } + + public BinaryString eval(BinaryString str) { + if (str == null) { + return null; + } + return BinaryString.fromString(StringUtils.strip(str.toString(), " ")); + } +} diff --git a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/udf/string/PadTest.java b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/udf/string/PadTest.java new file mode 100644 index 000000000..81bbbf109 --- /dev/null +++ b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/udf/string/PadTest.java @@ -0,0 +1,78 @@ +/* + * 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.string; + +import org.apache.geaflow.common.binary.BinaryString; +import org.apache.geaflow.dsl.udf.table.string.LPad; +import org.apache.geaflow.dsl.udf.table.string.RPad; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class PadTest { + + private static final BinaryString PAD = BinaryString.fromString("xy"); + + @Test + public void testLPad() { + LPad lPad = new LPad(); + Assert.assertEquals(lPad.eval("hi", 5, "xy"), "xyxhi"); + Assert.assertEquals(lPad.eval("hello", 3, "x"), "hel"); + Assert.assertEquals(lPad.eval("hi", 0, "x"), ""); + Assert.assertEquals(lPad.eval("hi", -1, "x"), ""); + Assert.assertEquals(lPad.eval("hi", 5, ""), "hi"); + Assert.assertNull(lPad.eval((String) null, 5, "x")); + Assert.assertNull(lPad.eval("hi", null, "x")); + Assert.assertNull(lPad.eval("hi", 5, (String) null)); + + Assert.assertEquals(lPad.eval(BinaryString.fromString("hi"), 5, PAD), + BinaryString.fromString("xyxhi")); + Assert.assertNull(lPad.eval((BinaryString) null, 5, PAD)); + } + + @Test + public void testRPad() { + RPad rPad = new RPad(); + Assert.assertEquals(rPad.eval("hi", 5, "xy"), "hixyx"); + Assert.assertEquals(rPad.eval("hello", 3, "x"), "hel"); + Assert.assertEquals(rPad.eval("hi", 0, "x"), ""); + Assert.assertEquals(rPad.eval("hi", -1, "x"), ""); + Assert.assertEquals(rPad.eval("hi", 5, ""), "hi"); + Assert.assertNull(rPad.eval((String) null, 5, "x")); + Assert.assertNull(rPad.eval("hi", null, "x")); + Assert.assertNull(rPad.eval("hi", 5, (String) null)); + + Assert.assertEquals(rPad.eval(BinaryString.fromString("hi"), 5, PAD), + BinaryString.fromString("hixyx")); + Assert.assertNull(rPad.eval((BinaryString) null, 5, PAD)); + } + + @Test + public void testUnicodeCodePoints() { + LPad lPad = new LPad(); + RPad rPad = new RPad(); + Assert.assertEquals(lPad.eval("\u80a1\u7968", 4, "\u661f"), + "\u661f\u661f\u80a1\u7968"); + Assert.assertEquals(rPad.eval("\u80a1\u7968", 4, "\u661f"), + "\u80a1\u7968\u661f\u661f"); + Assert.assertEquals(lPad.eval("\ud83d\ude00x", 1, "y"), "\ud83d\ude00"); + Assert.assertEquals(rPad.eval("x", 3, "\ud83d\ude00"), + "x\ud83d\ude00\ud83d\ude00"); + } +} diff --git a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/udf/string/TrimTest.java b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/udf/string/TrimTest.java index e05e7f5b9..5102fde08 100644 --- a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/udf/string/TrimTest.java +++ b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/udf/string/TrimTest.java @@ -22,6 +22,7 @@ import org.apache.geaflow.common.binary.BinaryString; import org.apache.geaflow.dsl.udf.table.string.LTrim; import org.apache.geaflow.dsl.udf.table.string.RTrim; +import org.apache.geaflow.dsl.udf.table.string.Trim; import org.testng.Assert; import org.testng.annotations.Test; @@ -43,4 +44,17 @@ public void testRLTrim() { Assert.assertEquals(rTrim.eval(BinaryString.fromString("abc ")), BinaryString.fromString("abc")); Assert.assertEquals(rTrim.eval(BinaryString.fromString(" ")), BinaryString.fromString("")); } + + @Test + public void testTrim() { + Trim trim = new Trim(); + Assert.assertEquals(trim.eval(" abc "), "abc"); + Assert.assertEquals(trim.eval("\tabc\t"), "\tabc\t"); + Assert.assertEquals(trim.eval(BinaryString.fromString(" abc ")), + BinaryString.fromString("abc")); + Assert.assertEquals(trim.eval(BinaryString.fromString("\tabc\t")), + BinaryString.fromString("\tabc\t")); + Assert.assertNull(trim.eval((String) null)); + Assert.assertNull(trim.eval((BinaryString) null)); + } } diff --git a/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/java/org/apache/geaflow/dsl/runtime/query/StringFunctionTest.java b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/java/org/apache/geaflow/dsl/runtime/query/StringFunctionTest.java new file mode 100644 index 000000000..dbc06e927 --- /dev/null +++ b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/java/org/apache/geaflow/dsl/runtime/query/StringFunctionTest.java @@ -0,0 +1,52 @@ +/* + * 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; + +import org.testng.annotations.Test; + +public class StringFunctionTest { + + @Test + public void testTrim() throws Exception { + QueryTester + .build() + .withQueryPath("/query/function_trim.sql") + .execute() + .checkSinkResult(); + } + + @Test + public void testLPad() throws Exception { + QueryTester + .build() + .withQueryPath("/query/function_lpad.sql") + .execute() + .checkSinkResult(); + } + + @Test + public void testRPad() throws Exception { + QueryTester + .build() + .withQueryPath("/query/function_rpad.sql") + .execute() + .checkSinkResult(); + } +} diff --git a/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/function_lpad.txt b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/function_lpad.txt new file mode 100644 index 000000000..cd869c2c2 --- /dev/null +++ b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/function_lpad.txt @@ -0,0 +1 @@ +xyxhi,hel,,hi,星星股票,null,x😀, diff --git a/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/function_rpad.txt b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/function_rpad.txt new file mode 100644 index 000000000..411bae2a4 --- /dev/null +++ b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/function_rpad.txt @@ -0,0 +1 @@ +hixyx,hel,,hi,股票星星,null,😀x, diff --git a/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/function_trim.txt b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/function_trim.txt new file mode 100644 index 000000000..586c50cef --- /dev/null +++ b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/function_trim.txt @@ -0,0 +1 @@ +hello,world,a b c,null,x hello x diff --git a/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/query/function_lpad.sql b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/query/function_lpad.sql new file mode 100644 index 000000000..743f57a7c --- /dev/null +++ b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/query/function_lpad.sql @@ -0,0 +1,43 @@ +/* + * 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. + */ + +CREATE TABLE output_console ( + c1 varchar, + c2 varchar, + c3 varchar, + c4 varchar, + c5 varchar, + c6 varchar, + c7 varchar, + c8 varchar +) WITH ( + type='file', + geaflow.dsl.file.path='${target}' +); + +INSERT INTO output_console +SELECT + lpad('hi', 5, 'xy'), + lpad('hello', 3, 'x'), + lpad('hi', 0, 'x'), + lpad('hi', 5, ''), + lpad('股票', 4, '星'), + lpad(cast(null as varchar), 5, 'x'), + lpad('😀', 2, 'x'), + lpad('hi', -1, 'x') diff --git a/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/query/function_rpad.sql b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/query/function_rpad.sql new file mode 100644 index 000000000..a852bf5a4 --- /dev/null +++ b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/query/function_rpad.sql @@ -0,0 +1,43 @@ +/* + * 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. + */ + +CREATE TABLE output_console ( + c1 varchar, + c2 varchar, + c3 varchar, + c4 varchar, + c5 varchar, + c6 varchar, + c7 varchar, + c8 varchar +) WITH ( + type='file', + geaflow.dsl.file.path='${target}' +); + +INSERT INTO output_console +SELECT + rpad('hi', 5, 'xy'), + rpad('hello', 3, 'x'), + rpad('hi', 0, 'x'), + rpad('hi', 5, ''), + rpad('股票', 4, '星'), + rpad(cast(null as varchar), 5, 'x'), + rpad('😀', 2, 'x'), + rpad('hi', -1, 'x') diff --git a/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/query/function_trim.sql b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/query/function_trim.sql new file mode 100644 index 000000000..f3d6e87cc --- /dev/null +++ b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/query/function_trim.sql @@ -0,0 +1,37 @@ +/* + * 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. + */ + +CREATE TABLE output_console ( + c1 varchar, + c2 varchar, + c3 varchar, + c4 varchar, + c5 varchar +) WITH ( + type='file', + geaflow.dsl.file.path='${target}' +); + +INSERT INTO output_console +SELECT + trim(' hello '), + trim('world'), + trim(' a b c '), + trim(cast(null as varchar)), + concat('x', trim(concat(ascii2string(9), 'hello', ascii2string(9))), 'x') From abd591a79f0a67d7995e1d03675b8a06faff25c9 Mon Sep 17 00:00:00 2001 From: zzq <914700349@qq.com> Date: Wed, 5 Aug 2026 17:40:03 +0800 Subject: [PATCH 2/2] fix: correct string function tests and semantics --- .../function/BuildInSqlFunctionTable.java | 2 - .../geaflow/dsl/udf/table/string/LPad.java | 4 +- .../geaflow/dsl/udf/table/string/RPad.java | 4 +- .../dsl/udf/table/string/StringPadUtil.java | 5 ++- .../geaflow/dsl/udf/table/string/Trim.java | 41 ------------------- .../geaflow/dsl/udf/string/PadTest.java | 6 ++- .../geaflow/dsl/udf/string/TrimTest.java | 14 ------- .../test/resources/expect/function_lpad.txt | 2 +- .../test/resources/expect/function_rpad.txt | 2 +- .../test/resources/query/function_trim.sql | 2 +- 10 files changed, 15 insertions(+), 67 deletions(-) delete mode 100644 geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/Trim.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 0d55790b4..9ef615893 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 @@ -130,7 +130,6 @@ import org.apache.geaflow.dsl.udf.table.string.Space; import org.apache.geaflow.dsl.udf.table.string.SplitEx; import org.apache.geaflow.dsl.udf.table.string.Substr; -import org.apache.geaflow.dsl.udf.table.string.Trim; import org.apache.geaflow.dsl.udf.table.string.UrlDecode; import org.apache.geaflow.dsl.udf.table.string.UrlEncode; import org.apache.geaflow.dsl.util.FunctionUtil; @@ -213,7 +212,6 @@ public class BuildInSqlFunctionTable extends ListSqlOperatorTable { .add(GeaFlowFunction.of(Space.class)) .add(GeaFlowFunction.of(SplitEx.class)) .add(GeaFlowFunction.of(Substr.class)) - .add(GeaFlowFunction.of(Trim.class)) .add(GeaFlowFunction.of(UrlDecode.class)) .add(GeaFlowFunction.of(UrlEncode.class)) .add(GeaFlowFunction.of(GetJsonObject.class)) diff --git a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/LPad.java b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/LPad.java index 24b01fcd0..527605d49 100644 --- a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/LPad.java +++ b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/LPad.java @@ -35,7 +35,7 @@ public BinaryString eval(BinaryString str, Integer length, BinaryString pad) { if (str == null || length == null || pad == null) { return null; } - return BinaryString.fromString(StringPadUtil.pad( - str.toString(), length, pad.toString(), true)); + String result = StringPadUtil.pad(str.toString(), length, pad.toString(), true); + return result == null ? null : BinaryString.fromString(result); } } diff --git a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/RPad.java b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/RPad.java index 93648570d..4d0d133ca 100644 --- a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/RPad.java +++ b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/RPad.java @@ -35,7 +35,7 @@ public BinaryString eval(BinaryString str, Integer length, BinaryString pad) { if (str == null || length == null || pad == null) { return null; } - return BinaryString.fromString(StringPadUtil.pad( - str.toString(), length, pad.toString(), false)); + String result = StringPadUtil.pad(str.toString(), length, pad.toString(), false); + return result == null ? null : BinaryString.fromString(result); } } diff --git a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/StringPadUtil.java b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/StringPadUtil.java index 6b1a67cf8..b09c349bd 100644 --- a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/StringPadUtil.java +++ b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/StringPadUtil.java @@ -28,7 +28,10 @@ static String pad(String str, Integer length, String pad, boolean left) { if (str == null || length == null || pad == null) { return null; } - if (length <= 0) { + if (length < 0) { + return null; + } + if (length == 0) { return ""; } diff --git a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/Trim.java b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/Trim.java deleted file mode 100644 index 90c63e45e..000000000 --- a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/main/java/org/apache/geaflow/dsl/udf/table/string/Trim.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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.string; - -import org.apache.commons.lang3.StringUtils; -import org.apache.geaflow.common.binary.BinaryString; -import org.apache.geaflow.dsl.common.function.Description; -import org.apache.geaflow.dsl.common.function.UDF; - -@Description(name = "trim", description = "Returns a string with leading and trailing " - + "spaces removed.") -public class Trim extends UDF { - - public String eval(String str) { - return StringUtils.strip(str, " "); - } - - public BinaryString eval(BinaryString str) { - if (str == null) { - return null; - } - return BinaryString.fromString(StringUtils.strip(str.toString(), " ")); - } -} diff --git a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/udf/string/PadTest.java b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/udf/string/PadTest.java index 81bbbf109..9972e9ba0 100644 --- a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/udf/string/PadTest.java +++ b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/udf/string/PadTest.java @@ -35,7 +35,7 @@ public void testLPad() { Assert.assertEquals(lPad.eval("hi", 5, "xy"), "xyxhi"); Assert.assertEquals(lPad.eval("hello", 3, "x"), "hel"); Assert.assertEquals(lPad.eval("hi", 0, "x"), ""); - Assert.assertEquals(lPad.eval("hi", -1, "x"), ""); + Assert.assertNull(lPad.eval("hi", -1, "x")); Assert.assertEquals(lPad.eval("hi", 5, ""), "hi"); Assert.assertNull(lPad.eval((String) null, 5, "x")); Assert.assertNull(lPad.eval("hi", null, "x")); @@ -43,6 +43,7 @@ public void testLPad() { Assert.assertEquals(lPad.eval(BinaryString.fromString("hi"), 5, PAD), BinaryString.fromString("xyxhi")); + Assert.assertNull(lPad.eval(BinaryString.fromString("hi"), -1, PAD)); Assert.assertNull(lPad.eval((BinaryString) null, 5, PAD)); } @@ -52,7 +53,7 @@ public void testRPad() { Assert.assertEquals(rPad.eval("hi", 5, "xy"), "hixyx"); Assert.assertEquals(rPad.eval("hello", 3, "x"), "hel"); Assert.assertEquals(rPad.eval("hi", 0, "x"), ""); - Assert.assertEquals(rPad.eval("hi", -1, "x"), ""); + Assert.assertNull(rPad.eval("hi", -1, "x")); Assert.assertEquals(rPad.eval("hi", 5, ""), "hi"); Assert.assertNull(rPad.eval((String) null, 5, "x")); Assert.assertNull(rPad.eval("hi", null, "x")); @@ -60,6 +61,7 @@ public void testRPad() { Assert.assertEquals(rPad.eval(BinaryString.fromString("hi"), 5, PAD), BinaryString.fromString("hixyx")); + Assert.assertNull(rPad.eval(BinaryString.fromString("hi"), -1, PAD)); Assert.assertNull(rPad.eval((BinaryString) null, 5, PAD)); } diff --git a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/udf/string/TrimTest.java b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/udf/string/TrimTest.java index 5102fde08..e05e7f5b9 100644 --- a/geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/udf/string/TrimTest.java +++ b/geaflow/geaflow-dsl/geaflow-dsl-plan/src/test/java/org/apache/geaflow/dsl/udf/string/TrimTest.java @@ -22,7 +22,6 @@ import org.apache.geaflow.common.binary.BinaryString; import org.apache.geaflow.dsl.udf.table.string.LTrim; import org.apache.geaflow.dsl.udf.table.string.RTrim; -import org.apache.geaflow.dsl.udf.table.string.Trim; import org.testng.Assert; import org.testng.annotations.Test; @@ -44,17 +43,4 @@ public void testRLTrim() { Assert.assertEquals(rTrim.eval(BinaryString.fromString("abc ")), BinaryString.fromString("abc")); Assert.assertEquals(rTrim.eval(BinaryString.fromString(" ")), BinaryString.fromString("")); } - - @Test - public void testTrim() { - Trim trim = new Trim(); - Assert.assertEquals(trim.eval(" abc "), "abc"); - Assert.assertEquals(trim.eval("\tabc\t"), "\tabc\t"); - Assert.assertEquals(trim.eval(BinaryString.fromString(" abc ")), - BinaryString.fromString("abc")); - Assert.assertEquals(trim.eval(BinaryString.fromString("\tabc\t")), - BinaryString.fromString("\tabc\t")); - Assert.assertNull(trim.eval((String) null)); - Assert.assertNull(trim.eval((BinaryString) null)); - } } diff --git a/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/function_lpad.txt b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/function_lpad.txt index cd869c2c2..f9020dbc6 100644 --- a/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/function_lpad.txt +++ b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/function_lpad.txt @@ -1 +1 @@ -xyxhi,hel,,hi,星星股票,null,x😀, +xyxhi,hel,,hi,星星股票,null,x😀,null diff --git a/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/function_rpad.txt b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/function_rpad.txt index 411bae2a4..d64280bfd 100644 --- a/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/function_rpad.txt +++ b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/expect/function_rpad.txt @@ -1 +1 @@ -hixyx,hel,,hi,股票星星,null,😀x, +hixyx,hel,,hi,股票星星,null,😀x,null diff --git a/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/query/function_trim.sql b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/query/function_trim.sql index f3d6e87cc..a4b76dcfd 100644 --- a/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/query/function_trim.sql +++ b/geaflow/geaflow-dsl/geaflow-dsl-runtime/src/test/resources/query/function_trim.sql @@ -34,4 +34,4 @@ SELECT trim('world'), trim(' a b c '), trim(cast(null as varchar)), - concat('x', trim(concat(ascii2string(9), 'hello', ascii2string(9))), 'x') + concat('x', trim(concat(ascii2str(9), 'hello', ascii2str(9))), 'x')