From 7ba40ca074443b7e25b1e323b7f44e78356fe047 Mon Sep 17 00:00:00 2001 From: zouxxyy Date: Sun, 11 Jan 2026 11:06:10 +0800 Subject: [PATCH] 1 --- .../org/apache/paimon/utils/StringUtils.java | 46 +++++++++ .../paimon/predicate/LeafPredicate.java | 8 +- .../paimon/predicate/PredicateBuilder.java | 2 +- .../paimon/predicate/PredicateTest.java | 13 +++ .../apache/paimon/utils/StringUtilsTest.java | 95 +++++++++++++++++++ 5 files changed, 160 insertions(+), 4 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 e20151f35e27..75f9cd2aabf7 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 @@ -19,6 +19,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; @@ -45,6 +46,9 @@ public class StringUtils { /** The empty String {@code ""}. */ public static final String EMPTY = ""; + /** Default maximum number of fields for truncated string representation. */ + public static final int DEFAULT_MAX_FIELDS = 25; + /** * Checks if the string is null, empty, or contains only whitespace characters. A whitespace * character is defined via {@link Character#isWhitespace(char)}. @@ -562,4 +566,46 @@ public static boolean isOpenBracket(char c) { public static boolean isCloseBracket(char c) { return c == ']' || c == '}' || c == ')'; } + + /** + * Converts a sequence to a string with truncation if it exceeds the maximum number of fields. + * This is useful for limiting the size of string representations of large collections. + * + * @param lst the collection to convert to string + * @param start the prefix string + * @param sep the separator between elements + * @param end the suffix string + * @param maxFields the maximum number of fields to include before truncation + * @return the truncated string representation + */ + public static String truncatedString( + Collection lst, String start, String sep, String end, int maxFields) { + boolean truncated = lst.size() > maxFields; + int numFields = truncated ? Math.max(0, maxFields - 1) : lst.size(); + + StringBuilder builder = new StringBuilder(); + builder.append(start); + + Iterator iterator = lst.iterator(); + for (int i = 0; i < numFields; i++) { + if (i > 0) { + builder.append(sep); + } + builder.append(iterator.next()); + } + + if (truncated) { + builder.append(sep) + .append("... ") + .append(lst.size() - numFields) + .append(" more fields"); + } + + builder.append(end); + return builder.toString(); + } + + public static String truncatedString(Collection lst, String start, String sep, String end) { + return truncatedString(lst, start, sep, end, DEFAULT_MAX_FIELDS); + } } diff --git a/paimon-common/src/main/java/org/apache/paimon/predicate/LeafPredicate.java b/paimon-common/src/main/java/org/apache/paimon/predicate/LeafPredicate.java index 6be94495564c..3ea68121c30a 100644 --- a/paimon-common/src/main/java/org/apache/paimon/predicate/LeafPredicate.java +++ b/paimon-common/src/main/java/org/apache/paimon/predicate/LeafPredicate.java @@ -26,6 +26,7 @@ import org.apache.paimon.io.DataInputViewStreamWrapper; import org.apache.paimon.io.DataOutputViewStreamWrapper; import org.apache.paimon.types.DataType; +import org.apache.paimon.utils.StringUtils; import java.io.IOException; import java.io.ObjectInputStream; @@ -119,12 +120,13 @@ public T visit(PredicateVisitor visitor) { @Override public String toString() { String literalsStr; - if (literals == null || literals.isEmpty()) { + int literalsSize = literals == null ? 0 : literals.size(); + if (literalsSize == 0) { literalsStr = ""; - } else if (literals.size() == 1) { + } else if (literalsSize == 1) { literalsStr = Objects.toString(literals.get(0)); } else { - literalsStr = literals.toString(); + literalsStr = StringUtils.truncatedString(literals, "[", ", ", "]"); } return literalsStr.isEmpty() ? function + "(" + fieldName() + ")" diff --git a/paimon-common/src/main/java/org/apache/paimon/predicate/PredicateBuilder.java b/paimon-common/src/main/java/org/apache/paimon/predicate/PredicateBuilder.java index 3e88a58b179f..58634f3edf94 100644 --- a/paimon-common/src/main/java/org/apache/paimon/predicate/PredicateBuilder.java +++ b/paimon-common/src/main/java/org/apache/paimon/predicate/PredicateBuilder.java @@ -197,7 +197,7 @@ private Predicate leaf(LeafFunction function, Transform transform) { public Predicate in(int idx, List literals) { // In the IN predicate, 20 literals are critical for performance. // If there are more than 20 literals, the performance will decrease. - if (literals.size() > 20 || literals.size() == 0) { + if (literals.size() > 20 || literals.isEmpty()) { DataField field = rowType.getFields().get(idx); return new LeafPredicate(In.INSTANCE, field.type(), idx, field.name(), literals); } diff --git a/paimon-common/src/test/java/org/apache/paimon/predicate/PredicateTest.java b/paimon-common/src/test/java/org/apache/paimon/predicate/PredicateTest.java index 5e011c6e7f56..e8ad446387ab 100644 --- a/paimon-common/src/test/java/org/apache/paimon/predicate/PredicateTest.java +++ b/paimon-common/src/test/java/org/apache/paimon/predicate/PredicateTest.java @@ -712,4 +712,17 @@ public void testPredicateToString() { .isEqualTo( "NotIn(f0, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21])"); } + + @Test + public void testPredicateToStringWithManyFields() { + PredicateBuilder builder = new PredicateBuilder(RowType.of(new IntType())); + List literals = new ArrayList<>(); + for (int i = 1; i <= 100; i++) { + literals.add(i); + } + Predicate p = builder.in(0, literals); + assertThat(p.toString()) + .isEqualTo( + "In(f0, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, ... 76 more fields])"); + } } diff --git a/paimon-common/src/test/java/org/apache/paimon/utils/StringUtilsTest.java b/paimon-common/src/test/java/org/apache/paimon/utils/StringUtilsTest.java index 10b56a97b7eb..b281812a13d7 100644 --- a/paimon-common/src/test/java/org/apache/paimon/utils/StringUtilsTest.java +++ b/paimon-common/src/test/java/org/apache/paimon/utils/StringUtilsTest.java @@ -411,6 +411,101 @@ void testIsNumericInvalidNumbers(String input) { } } + @Nested + class TruncatedStringTests { + + @Test + void testTruncatedStringWithinMaxFields() { + List items = Arrays.asList("a", "b", "c"); + String result = StringUtils.truncatedString(items, "[", ", ", "]", 5); + assertThat(result).isEqualTo("[a, b, c]"); + } + + @Test + void testTruncatedStringExactlyMaxFields() { + List items = Arrays.asList("a", "b", "c"); + String result = StringUtils.truncatedString(items, "[", ", ", "]", 3); + assertThat(result).isEqualTo("[a, b, c]"); + } + + @Test + void testTruncatedStringExceedsMaxFields() { + List items = Arrays.asList("a", "b", "c", "d", "e"); + String result = StringUtils.truncatedString(items, "[", ", ", "]", 3); + assertThat(result).isEqualTo("[a, b, ... 3 more fields]"); + } + + @Test + void testTruncatedStringExceedsMaxFieldsWithSeparator() { + List items = Arrays.asList(1, 2, 3, 4, 5, 6); + String result = StringUtils.truncatedString(items, "(", "-", ")", 4); + assertThat(result).isEqualTo("(1-2-3-... 3 more fields)"); + } + + @Test + void testTruncatedStringEmptyCollection() { + List items = Arrays.asList(); + String result = StringUtils.truncatedString(items, "[", ", ", "]", 3); + assertThat(result).isEqualTo("[]"); + } + + @Test + void testTruncatedStringSingleElement() { + List items = Arrays.asList("only"); + String result = StringUtils.truncatedString(items, "[", ", ", "]", 5); + assertThat(result).isEqualTo("[only]"); + } + + @Test + void testTruncatedStringMaxFieldsZero() { + List items = Arrays.asList("a", "b", "c"); + String result = StringUtils.truncatedString(items, "[", ", ", "]", 0); + assertThat(result).isEqualTo("[, ... 3 more fields]"); + } + + @Test + void testTruncatedStringMaxFieldsOne() { + List items = Arrays.asList("a", "b", "c", "d"); + String result = StringUtils.truncatedString(items, "[", ", ", "]", 1); + assertThat(result).isEqualTo("[, ... 4 more fields]"); + } + + @Test + void testTruncatedStringLargeCollection() { + List items = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + String result = StringUtils.truncatedString(items, "{", ", ", "}", 5); + assertThat(result).isEqualTo("{1, 2, 3, 4, ... 6 more fields}"); + } + + @Test + void testTruncatedStringWithEmptyStrings() { + List items = Arrays.asList("", "a", "", "b", ""); + String result = StringUtils.truncatedString(items, "[", "|", "]", 3); + assertThat(result).isEqualTo("[|a|... 3 more fields]"); + } + + @Test + void testTruncatedStringWithNullElements() { + List items = Arrays.asList("a", null, "b", "c"); + String result = StringUtils.truncatedString(items, "[", ", ", "]", 3); + assertThat(result).isEqualTo("[a, null, ... 2 more fields]"); + } + + @Test + void testTruncatedStringWithCustomDelimiters() { + List items = Arrays.asList("apple", "banana", "cherry", "date"); + String result = StringUtils.truncatedString(items, "<", " | ", ">", 3); + assertThat(result).isEqualTo(""); + } + + @Test + void testTruncatedStringWithEmptyDelimiters() { + List items = Arrays.asList("a", "b", "c"); + String result = StringUtils.truncatedString(items, "", "", "", 5); + assertThat(result).isEqualTo("abc"); + } + } + @Nested class EdgeCaseTests {