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

JingsongLi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git


The following commit(s) were added to refs/heads/master by this push:
     new bdd6e415d3 [parquet] Push down StartsWith as Parquet binary range 
(#9594)
bdd6e415d3 is described below

commit bdd6e415d3313f503522322e8ae58a9c469ab239
Author: sanshi <[email protected]>
AuthorDate: Thu Sep 3 22:08:14 2026 +0800

    [parquet] Push down StartsWith as Parquet binary range (#9594)
---
 .../parquet/filter2/predicate/ParquetFilters.java  | 30 ++++++++++++++++-
 .../paimon/format/parquet/ParquetFiltersTest.java  | 38 ++++++++++++++++++++++
 2 files changed, 67 insertions(+), 1 deletion(-)

diff --git 
a/paimon-format/src/main/java/org/apache/parquet/filter2/predicate/ParquetFilters.java
 
b/paimon-format/src/main/java/org/apache/parquet/filter2/predicate/ParquetFilters.java
index e28b8ee437..57d27578a1 100644
--- 
a/paimon-format/src/main/java/org/apache/parquet/filter2/predicate/ParquetFilters.java
+++ 
b/paimon-format/src/main/java/org/apache/parquet/filter2/predicate/ParquetFilters.java
@@ -186,7 +186,21 @@ public class ParquetFilters {
 
         @Override
         public FilterPredicate visitStartsWith(FieldRef fieldRef, Object 
literal) {
-            throw new UnsupportedOperationException();
+            Operators.Column<?> column = toParquetColumn(fieldRef);
+            if (!(column instanceof Operators.BinaryColumn)) {
+                throw new UnsupportedOperationException();
+            }
+
+            Binary prefix = (Binary) toParquetObject(literal, fieldRef);
+            if (prefix.length() == 0) {
+                throw new UnsupportedOperationException();
+            }
+
+            FilterPredicate lower = FilterApi.gtEq((Operators.BinaryColumn) 
column, prefix);
+            Binary upper = nextBinary(prefix);
+            return upper == null
+                    ? lower
+                    : FilterApi.and(lower, 
FilterApi.lt((Operators.BinaryColumn) column, upper));
         }
 
         @Override
@@ -418,6 +432,20 @@ public class ParquetFilters {
             throw new UnsupportedOperationException();
         }
 
+        /** Returns the smallest binary value strictly greater than all values 
with this prefix. */
+        @Nullable
+        private Binary nextBinary(Binary prefix) {
+            byte[] bytes = prefix.getBytes();
+            for (int i = bytes.length - 1; i >= 0; i--) {
+                int value = bytes[i] & 0xff;
+                if (value != 0xff) {
+                    bytes[i] = (byte) (value + 1);
+                    return Binary.fromConstantByteArray(bytes, 0, i + 1);
+                }
+            }
+            return null;
+        }
+
         private Decimal normalizeDecimal(Decimal decimal, DecimalType 
fieldType) {
             try {
                 BigDecimal normalized =
diff --git 
a/paimon-format/src/test/java/org/apache/paimon/format/parquet/ParquetFiltersTest.java
 
b/paimon-format/src/test/java/org/apache/paimon/format/parquet/ParquetFiltersTest.java
index e6f4c44260..fd677c3849 100644
--- 
a/paimon-format/src/test/java/org/apache/paimon/format/parquet/ParquetFiltersTest.java
+++ 
b/paimon-format/src/test/java/org/apache/paimon/format/parquet/ParquetFiltersTest.java
@@ -215,6 +215,44 @@ class ParquetFiltersTest {
                 true);
     }
 
+    @Test
+    public void testStartsWithIsPushedToParquet() {
+        RowType rowType =
+                new RowType(
+                        Collections.singletonList(new DataField(0, "string1", 
new VarCharType())));
+        MessageType schema = 
ParquetSchemaConverter.convertToParquetMessageType(rowType);
+        PredicateBuilder builder = new PredicateBuilder(rowType);
+
+        FilterCompat.Filter filter =
+                ParquetFilters.convert(
+                        Collections.singletonList(builder.startsWith(0, 
"abc")), schema, true);
+        assertThat(filter).isInstanceOf(FilterPredicateCompat.class);
+        FilterPredicate parquetPredicate = ((FilterPredicateCompat) 
filter).getFilterPredicate();
+        assertThat(parquetPredicate)
+                .isEqualTo(
+                        FilterApi.and(
+                                FilterApi.gtEq(
+                                        FilterApi.binaryColumn("string1"),
+                                        Binary.fromString("abc")),
+                                FilterApi.lt(
+                                        FilterApi.binaryColumn("string1"),
+                                        Binary.fromString("abd"))));
+    }
+
+    @Test
+    public void testEndsWithIsNotPushedToParquet() {
+        RowType rowType =
+                new RowType(
+                        Collections.singletonList(new DataField(0, "string1", 
new VarCharType())));
+        MessageType schema = 
ParquetSchemaConverter.convertToParquetMessageType(rowType);
+        PredicateBuilder builder = new PredicateBuilder(rowType);
+
+        FilterCompat.Filter filter =
+                ParquetFilters.convert(
+                        Collections.singletonList(builder.endsWith(0, "abc")), 
schema, true);
+        assertThat(filter).isEqualTo(FilterCompat.NOOP);
+    }
+
     @Test
     public void testInFilterLong() {
         RowType rowType =

Reply via email to