From 25d63efdd8789ac1c5680c1aab07c202d9d13e08 Mon Sep 17 00:00:00 2001 From: xuyu <11161569@vivo.com> Date: Wed, 11 Feb 2026 16:06:34 +0800 Subject: [PATCH 1/6] [spark] Introduce TrimTransform for trim/ltrim/rtrim --- .../org/apache/paimon/utils/StringUtils.java | 37 ++++- .../paimon/predicate/TrimTransform.java | 75 ++++++++++ .../paimon/predicate/TrimTransformTest.java | 136 ++++++++++++++++++ .../spark/util/SparkExpressionConverter.scala | 5 + .../spark/sql/PaimonPushDownTestBase.scala | 54 +++++++ 5 files changed, 301 insertions(+), 6 deletions(-) create mode 100644 paimon-common/src/main/java/org/apache/paimon/predicate/TrimTransform.java create mode 100644 paimon-common/src/test/java/org/apache/paimon/predicate/TrimTransformTest.java diff --git a/paimon-api/src/main/java/org/apache/paimon/utils/StringUtils.java b/paimon-api/src/main/java/org/apache/paimon/utils/StringUtils.java index 75f9cd2aabf7..e9094d377507 100644 --- a/paimon-api/src/main/java/org/apache/paimon/utils/StringUtils.java +++ b/paimon-api/src/main/java/org/apache/paimon/utils/StringUtils.java @@ -18,12 +18,7 @@ package org.apache.paimon.utils; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Objects; -import java.util.Random; +import java.util.*; import java.util.concurrent.ThreadLocalRandom; import static org.apache.paimon.utils.Preconditions.checkArgument; @@ -545,6 +540,36 @@ public static String trim(String value) { return value.trim(); } + public static String trim(String value, String charsToTrim) { + if (value == null || charsToTrim == null) { + return null; + } + return rtrim(ltrim(value, charsToTrim), charsToTrim); + } + + public static String ltrim(String value, String charsToTrim) { + if (value == null || charsToTrim == null) { + return null; + } + StringBuilder sb = new StringBuilder(value); + while (sb.length() > 0 && charsToTrim.contains(sb.substring(0, 1))) { + sb.deleteCharAt(0); + } + return sb.toString(); + } + + public static String rtrim(String value, String charsToTrim) { + if (value == null || charsToTrim == null) { + return null; + } + StringBuilder sb = new StringBuilder(value); + while (sb.length() > 0 + && charsToTrim.contains(sb.substring(sb.length() - 1, sb.length()))) { + sb.deleteCharAt(sb.length() - 1); + } + return sb.toString(); + } + public static String toUpperCase(String value) { if (value == null) { return null; diff --git a/paimon-common/src/main/java/org/apache/paimon/predicate/TrimTransform.java b/paimon-common/src/main/java/org/apache/paimon/predicate/TrimTransform.java new file mode 100644 index 000000000000..ad2279ee227a --- /dev/null +++ b/paimon-common/src/main/java/org/apache/paimon/predicate/TrimTransform.java @@ -0,0 +1,75 @@ +/* + * 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.paimon.predicate; + +import org.apache.paimon.data.BinaryString; +import org.apache.paimon.utils.StringUtils; + +import java.util.List; + +import static org.apache.paimon.utils.Preconditions.checkArgument; + +/** TRIM/LTRIM/RTRIM {@link Transform}. */ +public class TrimTransform extends StringTransform { + + private static final long serialVersionUID = 1L; + + public static final String NAME = "TRIM"; + + public static final String LTRIM = "LTRIM"; + + public static final String RTRIM = "RTRIM"; + + private final String trimWay; + + public TrimTransform(List inputs, String trimWay) { + super(inputs); + this.trimWay = trimWay; + checkArgument(inputs.size() == 1 || inputs.size() == 2); + } + + @Override + public String name() { + return NAME; + } + + @Override + public BinaryString transform(List inputs) { + if (inputs.get(0) == null) { + return null; + } + String sourceString = inputs.get(0).toString(); + String charsToTrim = inputs.size() == 1 ? " " : inputs.get(1).toString(); + switch (trimWay) { + case NAME: + return BinaryString.fromString(StringUtils.trim(sourceString, charsToTrim)); + case LTRIM: + return BinaryString.fromString(StringUtils.ltrim(sourceString, charsToTrim)); + case RTRIM: + return BinaryString.fromString(StringUtils.rtrim(sourceString, charsToTrim)); + default: + throw new IllegalArgumentException("Invalid trim way " + trimWay); + } + } + + @Override + public Transform copyWithNewInputs(List inputs) { + return new TrimTransform(inputs, this.trimWay); + } +} diff --git a/paimon-common/src/test/java/org/apache/paimon/predicate/TrimTransformTest.java b/paimon-common/src/test/java/org/apache/paimon/predicate/TrimTransformTest.java new file mode 100644 index 000000000000..1a4c7dcad829 --- /dev/null +++ b/paimon-common/src/test/java/org/apache/paimon/predicate/TrimTransformTest.java @@ -0,0 +1,136 @@ +/* + * 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.paimon.predicate; + +import org.apache.paimon.data.BinaryString; +import org.apache.paimon.data.GenericRow; +import org.apache.paimon.types.DataTypes; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TrimTransformTest { + + @Test + public void testNullInputs() { + List inputs = new ArrayList<>(); + // test for single argument + inputs.add(null); + TrimTransform transform = new TrimTransform(inputs, "TRIM"); + Object result = transform.transform(GenericRow.of()); + assertThat(result).isNull(); + + transform = new TrimTransform(inputs, "LTRIM"); + result = transform.transform(GenericRow.of()); + assertThat(result).isNull(); + + transform = new TrimTransform(inputs, "RTRIM"); + result = transform.transform(GenericRow.of()); + assertThat(result).isNull(); + + // test for binary argument + inputs.add(null); + transform = new TrimTransform(inputs, "TRIM"); + result = transform.transform(GenericRow.of()); + assertThat(result).isNull(); + + transform = new TrimTransform(inputs, "LTRIM"); + result = transform.transform(GenericRow.of()); + assertThat(result).isNull(); + + transform = new TrimTransform(inputs, "RTRIM"); + result = transform.transform(GenericRow.of()); + assertThat(result).isNull(); + } + + @Test + public void testNormalInputs() { + // test trim('cd', 'cddcaadccd') + List inputs = new ArrayList<>(); + inputs.add(BinaryString.fromString("cddcaadccd")); + inputs.add(BinaryString.fromString("cd")); + TrimTransform transform = new TrimTransform(inputs, "TRIM"); + Object result = transform.transform(GenericRow.of()); + assertThat(result).isEqualTo(BinaryString.fromString("aa")); + + // test ltrim('cd', 'cddcaadccd') + transform = new TrimTransform(inputs, "LTRIM"); + result = transform.transform(GenericRow.of()); + assertThat(result).isEqualTo(BinaryString.fromString("aadccd")); + + // test rtrim('cd', 'cddcaadccd') + transform = new TrimTransform(inputs, "RTRIM"); + result = transform.transform(GenericRow.of()); + assertThat(result).isEqualTo(BinaryString.fromString("cddcaa")); + + // test trim(' aa ') + inputs.clear(); + inputs.add(BinaryString.fromString(" aa ")); + transform = new TrimTransform(inputs, "TRIM"); + result = transform.transform(GenericRow.of()); + assertThat(result).isEqualTo(BinaryString.fromString("aa")); + + // test trim(' aa ') + transform = new TrimTransform(inputs, "LTRIM"); + result = transform.transform(GenericRow.of()); + assertThat(result).isEqualTo(BinaryString.fromString("aa ")); + + // test trim(' aa ') + transform = new TrimTransform(inputs, "RTRIM"); + result = transform.transform(GenericRow.of()); + assertThat(result).isEqualTo(BinaryString.fromString(" aa")); + } + + @Test + public void testSubstringRefInputs() { + List inputs = new ArrayList<>(); + inputs.add(new FieldRef(1, "f1", DataTypes.STRING())); + inputs.add(new FieldRef(2, "f2", DataTypes.STRING())); + TrimTransform transform = new TrimTransform(inputs, "TRIM"); + Object result = + transform.transform( + GenericRow.of( + BinaryString.fromString(""), + BinaryString.fromString("ahellob"), + BinaryString.fromString("ab"))); + assertThat(result).isEqualTo(BinaryString.fromString("hello")); + + transform = new TrimTransform(inputs, "LTRIM"); + result = + transform.transform( + GenericRow.of( + BinaryString.fromString(""), + BinaryString.fromString("ahellob"), + BinaryString.fromString("ab"))); + assertThat(result).isEqualTo(BinaryString.fromString("hellob")); + + transform = new TrimTransform(inputs, "RTRIM"); + result = + transform.transform( + GenericRow.of( + BinaryString.fromString(""), + BinaryString.fromString("ahellob"), + BinaryString.fromString("ab"))); + assertThat(result).isEqualTo(BinaryString.fromString("ahello")); + } +} diff --git a/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/util/SparkExpressionConverter.scala b/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/util/SparkExpressionConverter.scala index 347bde6513e9..bac268a7e9c2 100644 --- a/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/util/SparkExpressionConverter.scala +++ b/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/util/SparkExpressionConverter.scala @@ -39,6 +39,9 @@ object SparkExpressionConverter { private val UPPER = "UPPER" private val LOWER = "LOWER" private val SUBSTRING = "SUBSTRING" + private val TRIM = "TRIM" + private val LTRIM = "LTRIM" + private val RTRIM = "RTRIM" /** Convert Spark [[Expression]] to Paimon [[Transform]], return None if not supported. */ def toPaimonTransform(exp: Expression, rowType: RowType): Option[Transform] = { @@ -64,6 +67,8 @@ object SparkExpressionConverter { case UPPER => convertChildren(s.children()).map(i => new UpperTransform(i)) case LOWER => convertChildren(s.children()).map(i => new LowerTransform(i)) case SUBSTRING => convertChildren(s.children()).map(i => new SubstringTransform(i)) + case TRIM | LTRIM | RTRIM => + convertChildren(s.children()).map(i => new TrimTransform(i, s.name())) case _ => None } case c: Cast => diff --git a/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/PaimonPushDownTestBase.scala b/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/PaimonPushDownTestBase.scala index 747b140cb389..53ef2cae7878 100644 --- a/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/PaimonPushDownTestBase.scala +++ b/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/PaimonPushDownTestBase.scala @@ -130,6 +130,60 @@ abstract class PaimonPushDownTestBase extends PaimonSparkTestBase with AdaptiveS } } + test(s"Paimon push down: apply TRIM/LTRM/RTRIM") { + // Spark support push down TRIM/LTRM/RTRIM since Spark 3.4. + if (gteqSpark3_4) { + withTable("t") { + sql(""" + |CREATE TABLE t (id int, value int, dt STRING) + |using paimon + |PARTITIONED BY (dt) + |""".stripMargin) + + sql(""" + |INSERT INTO t values + |(1, 100, 'chelloc'), (1, 100, 'caa'), (1, 100, 'bbc') + |""".stripMargin) + + val q = + """ + |SELECT * FROM t + |WHERE TRIM('c', dt) = 'hello' + |""".stripMargin + assert(!checkFilterExists(q)) + + checkAnswer( + spark.sql(q), + Seq(Row(1, 100, "chelloc")) + ) + + val q1 = + """ + |SELECT * FROM t + |WHERE LTRIM('c', dt) = 'aa' + |""".stripMargin + assert(!checkFilterExists(q1)) + + checkAnswer( + spark.sql(q1), + Seq(Row(1, 100, "caa")) + ) + + val q2 = + """ + |SELECT * FROM t + |WHERE RTRIM('c', dt) = 'bb' + |""".stripMargin + assert(!checkFilterExists(q2)) + + checkAnswer( + spark.sql(q2), + Seq(Row(1, 100, "bbc")) + ) + } + } + } + test(s"Paimon push down: apply UPPER") { // Spark support push down UPPER since Spark 3.4. if (gteqSpark3_4) { From 465fb26eed3d0ab3df17019d64c1c28e6e13eb92 Mon Sep 17 00:00:00 2001 From: xuyu <11161569@vivo.com> Date: Wed, 11 Feb 2026 16:09:42 +0800 Subject: [PATCH 2/6] Fix style --- .../src/main/java/org/apache/paimon/utils/StringUtils.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/paimon-api/src/main/java/org/apache/paimon/utils/StringUtils.java b/paimon-api/src/main/java/org/apache/paimon/utils/StringUtils.java index e9094d377507..dce4530e2357 100644 --- a/paimon-api/src/main/java/org/apache/paimon/utils/StringUtils.java +++ b/paimon-api/src/main/java/org/apache/paimon/utils/StringUtils.java @@ -18,7 +18,12 @@ package org.apache.paimon.utils; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Random; import java.util.concurrent.ThreadLocalRandom; import static org.apache.paimon.utils.Preconditions.checkArgument; From 84495c29f3910efe922689b5374afa4e549d5257 Mon Sep 17 00:00:00 2001 From: xuyu <11161569@vivo.com> Date: Wed, 11 Feb 2026 16:12:19 +0800 Subject: [PATCH 3/6] Fix style --- .../java/org/apache/paimon/predicate/TrimTransformTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paimon-common/src/test/java/org/apache/paimon/predicate/TrimTransformTest.java b/paimon-common/src/test/java/org/apache/paimon/predicate/TrimTransformTest.java index 1a4c7dcad829..7a4ffb46ba0f 100644 --- a/paimon-common/src/test/java/org/apache/paimon/predicate/TrimTransformTest.java +++ b/paimon-common/src/test/java/org/apache/paimon/predicate/TrimTransformTest.java @@ -29,7 +29,7 @@ import static org.assertj.core.api.Assertions.assertThat; -public class TrimTransformTest { +class TrimTransformTest { @Test public void testNullInputs() { From 13b1bb85490b1593abbaeaf18dc31a4f0d07b087 Mon Sep 17 00:00:00 2001 From: xuyu <11161569@vivo.com> Date: Thu, 12 Feb 2026 10:02:45 +0800 Subject: [PATCH 4/6] add more tests --- .../paimon/spark/sql/PaimonPushDownTestBase.scala | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/PaimonPushDownTestBase.scala b/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/PaimonPushDownTestBase.scala index 53ef2cae7878..2f90e47c8515 100644 --- a/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/PaimonPushDownTestBase.scala +++ b/paimon-spark/paimon-spark-ut/src/test/scala/org/apache/paimon/spark/sql/PaimonPushDownTestBase.scala @@ -180,6 +180,18 @@ abstract class PaimonPushDownTestBase extends PaimonSparkTestBase with AdaptiveS spark.sql(q2), Seq(Row(1, 100, "bbc")) ) + + val q3 = + """ + |SELECT * FROM t + |WHERE TRIM(LEADING 'c' FROM dt) = 'aa' + |""".stripMargin + assert(!checkFilterExists(q2)) + + checkAnswer( + spark.sql(q3), + Seq(Row(1, 100, "caa")) + ) } } } From 9540a3a8e7abf913614785c42cfba04c0b638e72 Mon Sep 17 00:00:00 2001 From: xuyu <11161569@vivo.com> Date: Thu, 12 Feb 2026 18:45:06 +0800 Subject: [PATCH 5/6] Addressed --- .../org/apache/paimon/utils/StringUtils.java | 3 -- .../paimon/predicate/TrimTransform.java | 28 +++++++++-------- .../paimon/predicate/TrimTransformTest.java | 30 +++++++++---------- .../spark/util/SparkExpressionConverter.scala | 9 ++++-- 4 files changed, 37 insertions(+), 33 deletions(-) diff --git a/paimon-api/src/main/java/org/apache/paimon/utils/StringUtils.java b/paimon-api/src/main/java/org/apache/paimon/utils/StringUtils.java index dce4530e2357..c189de92e5d6 100644 --- a/paimon-api/src/main/java/org/apache/paimon/utils/StringUtils.java +++ b/paimon-api/src/main/java/org/apache/paimon/utils/StringUtils.java @@ -546,9 +546,6 @@ public static String trim(String value) { } public static String trim(String value, String charsToTrim) { - if (value == null || charsToTrim == null) { - return null; - } return rtrim(ltrim(value, charsToTrim), charsToTrim); } diff --git a/paimon-common/src/main/java/org/apache/paimon/predicate/TrimTransform.java b/paimon-common/src/main/java/org/apache/paimon/predicate/TrimTransform.java index ad2279ee227a..b7a2dc93a028 100644 --- a/paimon-common/src/main/java/org/apache/paimon/predicate/TrimTransform.java +++ b/paimon-common/src/main/java/org/apache/paimon/predicate/TrimTransform.java @@ -32,15 +32,11 @@ public class TrimTransform extends StringTransform { public static final String NAME = "TRIM"; - public static final String LTRIM = "LTRIM"; + private final Flag trimFlag; - public static final String RTRIM = "RTRIM"; - - private final String trimWay; - - public TrimTransform(List inputs, String trimWay) { + public TrimTransform(List inputs, Flag trimFlag) { super(inputs); - this.trimWay = trimWay; + this.trimFlag = trimFlag; checkArgument(inputs.size() == 1 || inputs.size() == 2); } @@ -56,20 +52,26 @@ public BinaryString transform(List inputs) { } String sourceString = inputs.get(0).toString(); String charsToTrim = inputs.size() == 1 ? " " : inputs.get(1).toString(); - switch (trimWay) { - case NAME: + switch (trimFlag) { + case BOTH: return BinaryString.fromString(StringUtils.trim(sourceString, charsToTrim)); - case LTRIM: + case LEADING: return BinaryString.fromString(StringUtils.ltrim(sourceString, charsToTrim)); - case RTRIM: + case TRAILING: return BinaryString.fromString(StringUtils.rtrim(sourceString, charsToTrim)); default: - throw new IllegalArgumentException("Invalid trim way " + trimWay); + throw new IllegalArgumentException("Invalid trim way " + trimFlag.name()); } } @Override public Transform copyWithNewInputs(List inputs) { - return new TrimTransform(inputs, this.trimWay); + return new TrimTransform(inputs, this.trimFlag); + } + + public enum Flag { + LEADING, + TRAILING, + BOTH } } diff --git a/paimon-common/src/test/java/org/apache/paimon/predicate/TrimTransformTest.java b/paimon-common/src/test/java/org/apache/paimon/predicate/TrimTransformTest.java index 7a4ffb46ba0f..b24fda78a7d7 100644 --- a/paimon-common/src/test/java/org/apache/paimon/predicate/TrimTransformTest.java +++ b/paimon-common/src/test/java/org/apache/paimon/predicate/TrimTransformTest.java @@ -36,29 +36,29 @@ public void testNullInputs() { List inputs = new ArrayList<>(); // test for single argument inputs.add(null); - TrimTransform transform = new TrimTransform(inputs, "TRIM"); + TrimTransform transform = new TrimTransform(inputs, TrimTransform.Flag.BOTH); Object result = transform.transform(GenericRow.of()); assertThat(result).isNull(); - transform = new TrimTransform(inputs, "LTRIM"); + transform = new TrimTransform(inputs, TrimTransform.Flag.LEADING); result = transform.transform(GenericRow.of()); assertThat(result).isNull(); - transform = new TrimTransform(inputs, "RTRIM"); + transform = new TrimTransform(inputs, TrimTransform.Flag.TRAILING); result = transform.transform(GenericRow.of()); assertThat(result).isNull(); // test for binary argument inputs.add(null); - transform = new TrimTransform(inputs, "TRIM"); + transform = new TrimTransform(inputs, TrimTransform.Flag.BOTH); result = transform.transform(GenericRow.of()); assertThat(result).isNull(); - transform = new TrimTransform(inputs, "LTRIM"); + transform = new TrimTransform(inputs, TrimTransform.Flag.LEADING); result = transform.transform(GenericRow.of()); assertThat(result).isNull(); - transform = new TrimTransform(inputs, "RTRIM"); + transform = new TrimTransform(inputs, TrimTransform.Flag.TRAILING); result = transform.transform(GenericRow.of()); assertThat(result).isNull(); } @@ -69,34 +69,34 @@ public void testNormalInputs() { List inputs = new ArrayList<>(); inputs.add(BinaryString.fromString("cddcaadccd")); inputs.add(BinaryString.fromString("cd")); - TrimTransform transform = new TrimTransform(inputs, "TRIM"); + TrimTransform transform = new TrimTransform(inputs, TrimTransform.Flag.BOTH); Object result = transform.transform(GenericRow.of()); assertThat(result).isEqualTo(BinaryString.fromString("aa")); // test ltrim('cd', 'cddcaadccd') - transform = new TrimTransform(inputs, "LTRIM"); + transform = new TrimTransform(inputs, TrimTransform.Flag.LEADING); result = transform.transform(GenericRow.of()); assertThat(result).isEqualTo(BinaryString.fromString("aadccd")); // test rtrim('cd', 'cddcaadccd') - transform = new TrimTransform(inputs, "RTRIM"); + transform = new TrimTransform(inputs, TrimTransform.Flag.TRAILING); result = transform.transform(GenericRow.of()); assertThat(result).isEqualTo(BinaryString.fromString("cddcaa")); // test trim(' aa ') inputs.clear(); inputs.add(BinaryString.fromString(" aa ")); - transform = new TrimTransform(inputs, "TRIM"); + transform = new TrimTransform(inputs, TrimTransform.Flag.BOTH); result = transform.transform(GenericRow.of()); assertThat(result).isEqualTo(BinaryString.fromString("aa")); // test trim(' aa ') - transform = new TrimTransform(inputs, "LTRIM"); + transform = new TrimTransform(inputs, TrimTransform.Flag.LEADING); result = transform.transform(GenericRow.of()); assertThat(result).isEqualTo(BinaryString.fromString("aa ")); // test trim(' aa ') - transform = new TrimTransform(inputs, "RTRIM"); + transform = new TrimTransform(inputs, TrimTransform.Flag.TRAILING); result = transform.transform(GenericRow.of()); assertThat(result).isEqualTo(BinaryString.fromString(" aa")); } @@ -106,7 +106,7 @@ public void testSubstringRefInputs() { List inputs = new ArrayList<>(); inputs.add(new FieldRef(1, "f1", DataTypes.STRING())); inputs.add(new FieldRef(2, "f2", DataTypes.STRING())); - TrimTransform transform = new TrimTransform(inputs, "TRIM"); + TrimTransform transform = new TrimTransform(inputs, TrimTransform.Flag.BOTH); Object result = transform.transform( GenericRow.of( @@ -115,7 +115,7 @@ public void testSubstringRefInputs() { BinaryString.fromString("ab"))); assertThat(result).isEqualTo(BinaryString.fromString("hello")); - transform = new TrimTransform(inputs, "LTRIM"); + transform = new TrimTransform(inputs, TrimTransform.Flag.LEADING); result = transform.transform( GenericRow.of( @@ -124,7 +124,7 @@ public void testSubstringRefInputs() { BinaryString.fromString("ab"))); assertThat(result).isEqualTo(BinaryString.fromString("hellob")); - transform = new TrimTransform(inputs, "RTRIM"); + transform = new TrimTransform(inputs, TrimTransform.Flag.TRAILING); result = transform.transform( GenericRow.of( diff --git a/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/util/SparkExpressionConverter.scala b/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/util/SparkExpressionConverter.scala index bac268a7e9c2..a5ff3598fbd5 100644 --- a/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/util/SparkExpressionConverter.scala +++ b/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/util/SparkExpressionConverter.scala @@ -67,8 +67,13 @@ object SparkExpressionConverter { case UPPER => convertChildren(s.children()).map(i => new UpperTransform(i)) case LOWER => convertChildren(s.children()).map(i => new LowerTransform(i)) case SUBSTRING => convertChildren(s.children()).map(i => new SubstringTransform(i)) - case TRIM | LTRIM | RTRIM => - convertChildren(s.children()).map(i => new TrimTransform(i, s.name())) + case TRIM => + convertChildren(s.children()).map(i => new TrimTransform(i, TrimTransform.Flag.BOTH)) + case LTRIM => + convertChildren(s.children()).map(i => new TrimTransform(i, TrimTransform.Flag.LEADING)) + case RTRIM => + convertChildren(s.children()).map( + i => new TrimTransform(i, TrimTransform.Flag.TRAILING)) case _ => None } case c: Cast => From 4745aae4809fd4c6b1e49c79fc9fa84e3cc735f1 Mon Sep 17 00:00:00 2001 From: xuyu <11161569@vivo.com> Date: Thu, 12 Feb 2026 18:49:20 +0800 Subject: [PATCH 6/6] add docs --- .../src/main/java/org/apache/paimon/predicate/TrimTransform.java | 1 + 1 file changed, 1 insertion(+) diff --git a/paimon-common/src/main/java/org/apache/paimon/predicate/TrimTransform.java b/paimon-common/src/main/java/org/apache/paimon/predicate/TrimTransform.java index b7a2dc93a028..6182335bb221 100644 --- a/paimon-common/src/main/java/org/apache/paimon/predicate/TrimTransform.java +++ b/paimon-common/src/main/java/org/apache/paimon/predicate/TrimTransform.java @@ -69,6 +69,7 @@ public Transform copyWithNewInputs(List inputs) { return new TrimTransform(inputs, this.trimFlag); } + /** Enum of trim functions. */ public enum Flag { LEADING, TRAILING,