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

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


The following commit(s) were added to refs/heads/master by this push:
     new 15f8c9a72 [core] Support filter pushdown in FilesTable (#2808)
15f8c9a72 is described below

commit 15f8c9a723123a8f482cce314aff657833be7919
Author: Jingsong Lee <[email protected]>
AuthorDate: Tue Jan 30 11:59:54 2024 +0800

    [core] Support filter pushdown in FilesTable (#2808)
---
 .../table/source/AbstractInnerTableScan.java       | 14 ++++
 .../apache/paimon/table/source/InnerTableScan.java |  9 +++
 .../paimon/table/source/InnerTableScanImpl.java    |  7 --
 .../apache/paimon/table/system/AuditLogTable.java  | 24 ++++++
 .../org/apache/paimon/table/system/FilesTable.java | 91 ++++++++++++++++++++--
 .../apache/paimon/table/system/FilesTableTest.java | 38 ++++++++-
 .../apache/paimon/flink/CatalogTableITCase.java    | 27 +++++++
 7 files changed, 197 insertions(+), 13 deletions(-)

diff --git 
a/paimon-core/src/main/java/org/apache/paimon/table/source/AbstractInnerTableScan.java
 
b/paimon-core/src/main/java/org/apache/paimon/table/source/AbstractInnerTableScan.java
index fd605b64e..16246c113 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/table/source/AbstractInnerTableScan.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/table/source/AbstractInnerTableScan.java
@@ -48,6 +48,7 @@ import org.apache.paimon.utils.Pair;
 import org.apache.paimon.utils.SnapshotManager;
 
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 
 import static org.apache.paimon.CoreOptions.FULL_COMPACTION_DELTA_COMMITS;
@@ -70,11 +71,24 @@ public abstract class AbstractInnerTableScan implements 
InnerTableScan {
         return this;
     }
 
+    @Override
     public AbstractInnerTableScan withBucketFilter(Filter<Integer> 
bucketFilter) {
         snapshotReader.withBucketFilter(bucketFilter);
         return this;
     }
 
+    @Override
+    public AbstractInnerTableScan withPartitionFilter(Map<String, String> 
partitionSpec) {
+        snapshotReader.withPartitionFilter(partitionSpec);
+        return this;
+    }
+
+    @Override
+    public AbstractInnerTableScan withLevelFilter(Filter<Integer> levelFilter) 
{
+        snapshotReader.withLevelFilter(levelFilter);
+        return this;
+    }
+
     public AbstractInnerTableScan withMetricsRegistry(MetricRegistry 
metricsRegistry) {
         snapshotReader.withMetricRegistry(metricsRegistry);
         return this;
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/table/source/InnerTableScan.java 
b/paimon-core/src/main/java/org/apache/paimon/table/source/InnerTableScan.java
index 234d82e52..613b2efc2 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/table/source/InnerTableScan.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/table/source/InnerTableScan.java
@@ -20,6 +20,7 @@ package org.apache.paimon.table.source;
 
 import org.apache.paimon.metrics.MetricRegistry;
 import org.apache.paimon.predicate.Predicate;
+import org.apache.paimon.utils.Filter;
 
 import java.util.Map;
 
@@ -36,6 +37,14 @@ public interface InnerTableScan extends TableScan {
         return this;
     }
 
+    default InnerTableScan withBucketFilter(Filter<Integer> bucketFilter) {
+        return this;
+    }
+
+    default InnerTableScan withLevelFilter(Filter<Integer> levelFilter) {
+        return this;
+    }
+
     default InnerTableScan withMetricsRegistry(MetricRegistry metricRegistry) {
         // do nothing, should implement this if need
         return this;
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/table/source/InnerTableScanImpl.java
 
b/paimon-core/src/main/java/org/apache/paimon/table/source/InnerTableScanImpl.java
index a30d82f8c..1e892fe1f 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/table/source/InnerTableScanImpl.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/table/source/InnerTableScanImpl.java
@@ -30,7 +30,6 @@ import javax.annotation.Nullable;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 
 /** {@link TableScan} implementation for batch planning. */
 public class InnerTableScanImpl extends AbstractInnerTableScan {
@@ -60,12 +59,6 @@ public class InnerTableScanImpl extends 
AbstractInnerTableScan {
         return this;
     }
 
-    @Override
-    public InnerTableScan withPartitionFilter(Map<String, String> 
partitionSpec) {
-        snapshotReader.withPartitionFilter(partitionSpec);
-        return this;
-    }
-
     @Override
     public InnerTableScan withLimit(int limit) {
         this.pushDownLimit = limit;
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/table/system/AuditLogTable.java 
b/paimon-core/src/main/java/org/apache/paimon/table/system/AuditLogTable.java
index fead5068b..96c2621b6 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/table/system/AuditLogTable.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/table/system/AuditLogTable.java
@@ -315,6 +315,30 @@ public class AuditLogTable implements DataTable, 
ReadonlyTable {
             return this;
         }
 
+        @Override
+        public InnerTableScan withLimit(int limit) {
+            batchScan.withLimit(limit);
+            return this;
+        }
+
+        @Override
+        public InnerTableScan withPartitionFilter(Map<String, String> 
partitionSpec) {
+            batchScan.withPartitionFilter(partitionSpec);
+            return this;
+        }
+
+        @Override
+        public InnerTableScan withBucketFilter(Filter<Integer> bucketFilter) {
+            batchScan.withBucketFilter(bucketFilter);
+            return this;
+        }
+
+        @Override
+        public InnerTableScan withLevelFilter(Filter<Integer> levelFilter) {
+            batchScan.withLevelFilter(levelFilter);
+            return this;
+        }
+
         @Override
         public Plan plan() {
             return batchScan.plan();
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/table/system/FilesTable.java 
b/paimon-core/src/main/java/org/apache/paimon/table/system/FilesTable.java
index 0949de2df..899e7e4e4 100644
--- a/paimon-core/src/main/java/org/apache/paimon/table/system/FilesTable.java
+++ b/paimon-core/src/main/java/org/apache/paimon/table/system/FilesTable.java
@@ -19,13 +19,17 @@
 package org.apache.paimon.table.system;
 
 import org.apache.paimon.data.BinaryString;
+import org.apache.paimon.data.GenericRow;
 import org.apache.paimon.data.InternalRow;
 import org.apache.paimon.data.LazyGenericRow;
 import org.apache.paimon.disk.IOManager;
 import org.apache.paimon.format.FieldStats;
 import org.apache.paimon.io.DataFileMeta;
 import org.apache.paimon.io.DataFilePathFactory;
+import org.apache.paimon.predicate.Equal;
+import org.apache.paimon.predicate.LeafPredicate;
 import org.apache.paimon.predicate.Predicate;
+import org.apache.paimon.predicate.PredicateBuilder;
 import org.apache.paimon.reader.RecordReader;
 import org.apache.paimon.schema.SchemaManager;
 import org.apache.paimon.schema.TableSchema;
@@ -54,12 +58,15 @@ import org.apache.paimon.utils.SerializationUtils;
 
 import org.apache.paimon.shade.guava30.com.google.common.collect.Iterators;
 
+import javax.annotation.Nullable;
+
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -142,19 +149,47 @@ public class FilesTable implements ReadonlyTable {
 
         private final FileStoreTable storeTable;
 
+        @Nullable private LeafPredicate partitionPredicate;
+        @Nullable private LeafPredicate bucketPredicate;
+        @Nullable private LeafPredicate levelPredicate;
+
         private FilesScan(FileStoreTable storeTable) {
             this.storeTable = storeTable;
         }
 
         @Override
-        public InnerTableScan withFilter(Predicate predicate) {
-            // TODO
+        public InnerTableScan withFilter(Predicate pushdown) {
+            List<Predicate> predicates = PredicateBuilder.splitAnd(pushdown);
+            for (Predicate predicate : predicates) {
+                if (predicate instanceof LeafPredicate) {
+                    LeafPredicate leaf = (LeafPredicate) predicate;
+                    switch (leaf.fieldName()) {
+                        case "partition":
+                            this.partitionPredicate = leaf;
+                            break;
+                        case "bucket":
+                            this.bucketPredicate = leaf;
+                            break;
+                        case "level":
+                            this.levelPredicate = leaf;
+                            break;
+                        default:
+                            break;
+                    }
+                }
+            }
             return this;
         }
 
         @Override
         public Plan innerPlan() {
-            return () -> Collections.singletonList(new FilesSplit(storeTable));
+            return () ->
+                    Collections.singletonList(
+                            new FilesSplit(
+                                    storeTable,
+                                    partitionPredicate,
+                                    bucketPredicate,
+                                    levelPredicate));
         }
     }
 
@@ -164,8 +199,19 @@ public class FilesTable implements ReadonlyTable {
 
         private final FileStoreTable storeTable;
 
-        private FilesSplit(FileStoreTable storeTable) {
+        @Nullable private final LeafPredicate partitionPredicate;
+        @Nullable private final LeafPredicate bucketPredicate;
+        @Nullable private final LeafPredicate levelPredicate;
+
+        private FilesSplit(
+                FileStoreTable storeTable,
+                @Nullable LeafPredicate partitionPredicate,
+                @Nullable LeafPredicate bucketPredicate,
+                @Nullable LeafPredicate levelPredicate) {
             this.storeTable = storeTable;
+            this.partitionPredicate = partitionPredicate;
+            this.bucketPredicate = bucketPredicate;
+            this.levelPredicate = levelPredicate;
         }
 
         @Override
@@ -178,7 +224,42 @@ public class FilesTable implements ReadonlyTable {
         }
 
         private TableScan.Plan plan() {
-            return storeTable.newScan().plan();
+            InnerTableScan scan = storeTable.newScan();
+            if (partitionPredicate != null) {
+                if (partitionPredicate.function() instanceof Equal) {
+                    String partitionStr = 
partitionPredicate.literals().get(0).toString();
+                    if (partitionStr.startsWith("[")) {
+                        partitionStr = partitionStr.substring(1);
+                    }
+                    if (partitionStr.endsWith("]")) {
+                        partitionStr = partitionStr.substring(0, 
partitionStr.length() - 1);
+                    }
+                    String[] partFields = partitionStr.split(", ");
+                    LinkedHashMap<String, String> partSpec = new 
LinkedHashMap<>();
+                    List<String> partitionKeys = storeTable.partitionKeys();
+                    for (int i = 0; i < partitionKeys.size(); i++) {
+                        partSpec.put(partitionKeys.get(i), partFields[i]);
+                    }
+                    scan.withPartitionFilter(partSpec);
+                }
+                // TODO support range?
+            }
+            if (bucketPredicate != null) {
+                scan.withBucketFilter(
+                        bucket -> {
+                            // bucket index: 1
+                            return bucketPredicate.test(GenericRow.of(null, 
bucket));
+                        });
+            }
+            if (levelPredicate != null) {
+                scan.withLevelFilter(
+                        level -> {
+                            // level index: 5
+                            return levelPredicate.test(
+                                    GenericRow.of(null, null, null, null, 
null, level));
+                        });
+            }
+            return scan.plan();
         }
 
         @Override
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/table/system/FilesTableTest.java 
b/paimon-core/src/test/java/org/apache/paimon/table/system/FilesTableTest.java
index da8730861..068d5d33a 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/table/system/FilesTableTest.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/table/system/FilesTableTest.java
@@ -31,6 +31,8 @@ import org.apache.paimon.io.DataFileMeta;
 import org.apache.paimon.manifest.FileKind;
 import org.apache.paimon.manifest.ManifestEntry;
 import org.apache.paimon.operation.FileStoreScan;
+import org.apache.paimon.predicate.Predicate;
+import org.apache.paimon.predicate.PredicateBuilder;
 import org.apache.paimon.schema.Schema;
 import org.apache.paimon.schema.SchemaManager;
 import org.apache.paimon.schema.SchemaUtils;
@@ -38,6 +40,7 @@ import org.apache.paimon.schema.TableSchema;
 import org.apache.paimon.table.FileStoreTable;
 import org.apache.paimon.table.FileStoreTableFactory;
 import org.apache.paimon.table.TableTestBase;
+import org.apache.paimon.table.source.ReadBuilder;
 import org.apache.paimon.types.DataTypes;
 import org.apache.paimon.utils.SnapshotManager;
 
@@ -45,17 +48,18 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
+import static org.apache.paimon.io.DataFileTestUtils.row;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 /** Tests for {@link FilesTable}. */
 public class FilesTableTest extends TableTestBase {
-    private static final String tableName = "MyTable";
 
     private FileStoreTable table;
     private FileStoreScan scan;
@@ -64,6 +68,7 @@ public class FilesTableTest extends TableTestBase {
 
     @BeforeEach
     public void before() throws Exception {
+        String tableName = "MyTable";
         FileIO fileIO = LocalFileIO.create();
         Path tablePath = new Path(String.format("%s/%s.db/%s", warehouse, 
database, tableName));
         Schema schema =
@@ -94,6 +99,37 @@ public class FilesTableTest extends TableTestBase {
         write(table, GenericRow.of(2, 1, 3), GenericRow.of(2, 2, 4));
     }
 
+    @Test
+    public void testReadWithFilter() throws Exception {
+        compact(table, row(2), 0);
+        write(table, GenericRow.of(3, 1, 1));
+        assertThat(readPartBucketLevel(null))
+                .containsExactlyInAnyOrder("[1]-0-0", "[1]-0-0", "[1]-1-0", 
"[2]-0-5");
+
+        PredicateBuilder builder = new PredicateBuilder(FilesTable.TABLE_TYPE);
+        assertThat(readPartBucketLevel(builder.equal(0, "[2]")))
+                .containsExactlyInAnyOrder("[2]-0-5");
+        assertThat(readPartBucketLevel(builder.equal(1, 
1))).containsExactlyInAnyOrder("[1]-1-0");
+        assertThat(readPartBucketLevel(builder.equal(5, 
5))).containsExactlyInAnyOrder("[2]-0-5");
+    }
+
+    private List<String> readPartBucketLevel(Predicate predicate) throws 
IOException {
+        ReadBuilder readBuilder = 
filesTable.newReadBuilder().withFilter(predicate);
+        List<String> rows = new ArrayList<>();
+        readBuilder
+                .newRead()
+                .createReader(readBuilder.newScan().plan())
+                .forEachRemaining(
+                        row ->
+                                rows.add(
+                                        row.getString(0)
+                                                + "-"
+                                                + row.getInt(1)
+                                                + "-"
+                                                + row.getInt(5)));
+        return rows;
+    }
+
     @Test
     public void testReadFilesFromLatest() throws Exception {
         List<InternalRow> expectedRow = getExceptedResult(2L);
diff --git 
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/CatalogTableITCase.java
 
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/CatalogTableITCase.java
index 175a438bd..150caf7b0 100644
--- 
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/CatalogTableITCase.java
+++ 
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/CatalogTableITCase.java
@@ -37,6 +37,7 @@ import javax.annotation.Nonnull;
 import java.util.List;
 import java.util.stream.Collectors;
 
+import static 
org.apache.flink.table.api.config.TableConfigOptions.TABLE_DML_SYNC;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatCode;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -630,6 +631,32 @@ public class CatalogTableITCase extends CatalogITCaseBase {
                                                 : "[5],[10]")));
     }
 
+    @Test
+    public void testFilesTableWithFilter() {
+        tEnv.getConfig().set(TABLE_DML_SYNC, true);
+        sql(
+                "CREATE TABLE T_WITH_FILTER (k INT, p INT, v INT, PRIMARY KEY 
(k, p) NOT ENFORCED) "
+                        + "PARTITIONED BY (p) WITH ('bucket'='2')");
+        sql("INSERT INTO T_WITH_FILTER VALUES (1, 2, 3), (4, 5, 6)");
+        sql("CALL sys.compact('default.T_WITH_FILTER')");
+        sql("INSERT INTO T_WITH_FILTER VALUES (7, 8, 9)");
+
+        assertThat(sql("SELECT `partition`, bucket, level FROM 
T_WITH_FILTER$files"))
+                .containsExactlyInAnyOrder(
+                        Row.of("[2]", 0, 5), Row.of("[5]", 0, 5), 
Row.of("[8]", 1, 0));
+
+        assertThat(
+                        sql(
+                                "SELECT `partition`, bucket, level FROM 
T_WITH_FILTER$files WHERE `partition`='[2]'"))
+                .containsExactlyInAnyOrder(Row.of("[2]", 0, 5));
+
+        assertThat(sql("SELECT `partition`, bucket, level FROM 
T_WITH_FILTER$files WHERE bucket=0"))
+                .containsExactlyInAnyOrder(Row.of("[2]", 0, 5), Row.of("[5]", 
0, 5));
+
+        assertThat(sql("SELECT `partition`, bucket, level FROM 
T_WITH_FILTER$files WHERE level=0"))
+                .containsExactlyInAnyOrder(Row.of("[8]", 1, 0));
+    }
+
     @Nonnull
     private List<String> getRowStringList(List<Row> rows) {
         return rows.stream()

Reply via email to