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 d0f993e8e7 [mosaic] Resolve row-group filter fields by name (#10729)
d0f993e8e7 is described below
commit d0f993e8e781000fb9318932106942b3bed84e7e
Author: Eunbin Son <[email protected]>
AuthorDate: Mon Oct 26 13:20:41 2026 +0900
[mosaic] Resolve row-group filter fields by name (#10729)
---
.../paimon/format/mosaic/MosaicRecordsReader.java | 25 ++++++++-
.../format/mosaic/FormatTableMosaicReadTest.java | 59 ++++++++++++++++++++++
2 files changed, 83 insertions(+), 1 deletion(-)
diff --git
a/paimon-mosaic/src/main/java/org/apache/paimon/format/mosaic/MosaicRecordsReader.java
b/paimon-mosaic/src/main/java/org/apache/paimon/format/mosaic/MosaicRecordsReader.java
index f0e1018b90..9b1075262f 100644
---
a/paimon-mosaic/src/main/java/org/apache/paimon/format/mosaic/MosaicRecordsReader.java
+++
b/paimon-mosaic/src/main/java/org/apache/paimon/format/mosaic/MosaicRecordsReader.java
@@ -31,6 +31,7 @@ import org.apache.paimon.fs.Path;
import org.apache.paimon.mosaic.ColumnStatistics;
import org.apache.paimon.mosaic.MosaicReader;
import org.apache.paimon.predicate.Predicate;
+import org.apache.paimon.predicate.PredicateRemapper;
import org.apache.paimon.reader.FileRecordIterator;
import org.apache.paimon.reader.FileRecordReader;
import org.apache.paimon.table.SpecialFields;
@@ -214,7 +215,7 @@ public class MosaicRecordsReader implements
FileRecordReader<InternalRow> {
this.inputFileAdapter = inputFileAdapter;
this.dataSchemaRowType = dataSchemaRowType;
this.projectedFieldCount = projectedRowType.getFieldCount();
- this.predicates = predicates;
+ this.predicates = resolvePredicates(predicates, dataSchemaRowType);
this.selection = selection;
this.allocator = allocator;
@@ -298,6 +299,28 @@ public class MosaicRecordsReader implements
FileRecordReader<InternalRow> {
return Math.max(1, bytes);
}
+ /**
+ * Resolves predicate fields by name against the file's row type. Callers
such as Format Tables
+ * and primary-key tables pass predicates whose indices refer to a
different row type.
+ */
+ @Nullable
+ private static List<Predicate> resolvePredicates(
+ @Nullable List<Predicate> predicates, RowType dataSchemaRowType) {
+ if (predicates == null || predicates.isEmpty()) {
+ return predicates;
+ }
+ List<Predicate> resolved = new ArrayList<>(predicates.size());
+ for (Predicate predicate : predicates) {
+ try {
+ resolved.add(PredicateRemapper.remap(predicate,
dataSchemaRowType));
+ } catch (IllegalArgumentException e) {
+ // The field is not in this file's row type. Dropping the
conjunct only relaxes
+ // row-group pruning.
+ }
+ }
+ return resolved;
+ }
+
int prefetchDepth() {
return prefetchDepth;
}
diff --git
a/paimon-mosaic/src/test/java/org/apache/paimon/format/mosaic/FormatTableMosaicReadTest.java
b/paimon-mosaic/src/test/java/org/apache/paimon/format/mosaic/FormatTableMosaicReadTest.java
index db88d765d8..8384d66a48 100644
---
a/paimon-mosaic/src/test/java/org/apache/paimon/format/mosaic/FormatTableMosaicReadTest.java
+++
b/paimon-mosaic/src/test/java/org/apache/paimon/format/mosaic/FormatTableMosaicReadTest.java
@@ -25,6 +25,8 @@ import org.apache.paimon.data.InternalRow;
import org.apache.paimon.data.serializer.InternalRowSerializer;
import org.apache.paimon.fs.Path;
import org.apache.paimon.fs.local.LocalFileIO;
+import org.apache.paimon.predicate.Predicate;
+import org.apache.paimon.predicate.PredicateBuilder;
import org.apache.paimon.reader.RecordReader;
import org.apache.paimon.table.FormatTable;
import org.apache.paimon.table.sink.BatchTableCommit;
@@ -40,6 +42,8 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
+import javax.annotation.Nullable;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -186,6 +190,53 @@ class FormatTableMosaicReadTest {
.hasMessageContaining("Mosaic format only supports zstd");
}
+ @Test
+ void testFilterOnDataColumnAfterPartitionColumn() throws Exception {
+ RowType rowType = partitionFirstRowType();
+ FormatTable table = writePartitionFirstTable(rowType, "a,b");
+ PredicateBuilder builder = new PredicateBuilder(rowType);
+
+ assertThat(readAll(table, rowType, builder.equal(1, 1)))
+ .extracting(row -> row.getInt(1))
+ .contains(1);
+ assertThat(readAll(table, rowType, builder.equal(2, 101)))
+ .extracting(row -> row.getInt(2))
+ .contains(101);
+ assertThat(readAll(table, rowType, builder.greaterOrEqual(1,
105))).isEmpty();
+ }
+
+ @Test
+ void testFilterOnColumnWithoutStatsAfterPartitionColumn() throws Exception
{
+ RowType rowType = partitionFirstRowType();
+ FormatTable table = writePartitionFirstTable(rowType, "b");
+ PredicateBuilder builder = new PredicateBuilder(rowType);
+
+ assertThat(readAll(table, rowType, builder.equal(1, 1)))
+ .extracting(row -> row.getInt(1))
+ .contains(1);
+ }
+
+ private static RowType partitionFirstRowType() {
+ return RowType.builder()
+ .field("dt", DataTypes.STRING())
+ .field("a", DataTypes.INT())
+ .field("b", DataTypes.INT())
+ .build();
+ }
+
+ private FormatTable writePartitionFirstTable(RowType rowType, String
statsColumns)
+ throws Exception {
+ Map<String, String> options = new HashMap<>();
+ options.put(MosaicFileFormat.STATS_COLUMNS.key(), statsColumns);
+ FormatTable table = buildFormatTable(rowType,
Collections.singletonList("dt"), options);
+ List<InternalRow> rows = new ArrayList<>();
+ for (int i = 1; i <= 10; i++) {
+ rows.add(GenericRow.of(BinaryString.fromString("p"), i, 100 + i));
+ }
+ writeAll(table, rows);
+ return table;
+ }
+
private FormatTable buildFormatTable(
RowType rowType, List<String> partitionKeys, Map<String, String>
options) {
String location = new Path(tempPath.toUri()).toString();
@@ -218,7 +269,15 @@ class FormatTableMosaicReadTest {
}
private static List<InternalRow> readAll(FormatTable table, RowType
rowType) throws Exception {
+ return readAll(table, rowType, null);
+ }
+
+ private static List<InternalRow> readAll(
+ FormatTable table, RowType rowType, @Nullable Predicate filter)
throws Exception {
ReadBuilder readBuilder = table.newReadBuilder();
+ if (filter != null) {
+ readBuilder.withFilter(filter);
+ }
List<Split> splits = readBuilder.newScan().plan().splits();
assertThat(splits).isNotEmpty();