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 29549c8084 [core] Fix snapshots table failing IN filter for unknown id 
(#9134)
29549c8084 is described below

commit 29549c8084e04f326fe66b650956344a4c9ed1f3
Author: Arnav Balyan <[email protected]>
AuthorDate: Mon Aug 17 11:05:01 2026 +0530

    [core] Fix snapshots table failing IN filter for unknown id (#9134)
---
 .../org/apache/paimon/schema/SchemaManager.java    |  4 +++
 .../apache/paimon/table/system/SchemasTable.java   | 14 +++++++-
 .../org/apache/paimon/utils/SnapshotManager.java   | 18 +++++++---
 .../paimon/table/system/SchemasTableTest.java      | 40 ++++++++++++++++++++++
 .../paimon/table/system/SnapshotsTableTest.java    | 40 ++++++++++++++++++++++
 .../apache/paimon/utils/SnapshotManagerTest.java   | 14 ++++++++
 .../apache/paimon/flink/CatalogTableITCase.java    | 12 +++----
 .../paimon/hive/FlinkGenericCatalogITCase.java     | 11 +++---
 8 files changed, 134 insertions(+), 19 deletions(-)

diff --git 
a/paimon-core/src/main/java/org/apache/paimon/schema/SchemaManager.java 
b/paimon-core/src/main/java/org/apache/paimon/schema/SchemaManager.java
index 28175245af..2c83cbcb57 100644
--- a/paimon-core/src/main/java/org/apache/paimon/schema/SchemaManager.java
+++ b/paimon-core/src/main/java/org/apache/paimon/schema/SchemaManager.java
@@ -1227,6 +1227,10 @@ public class SchemaManager implements Serializable {
         return fromPath(fileIO, toSchemaPath(id));
     }
 
+    public TableSchema tryGetSchema(long id) throws FileNotFoundException {
+        return tryFromPath(fileIO, toSchemaPath(id));
+    }
+
     /** Check if a schema exists. */
     public boolean schemaExists(long id) {
         Path path = toSchemaPath(id);
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/table/system/SchemasTable.java 
b/paimon-core/src/main/java/org/apache/paimon/table/system/SchemasTable.java
index b5093c3aeb..9c3fd9ca61 100644
--- a/paimon-core/src/main/java/org/apache/paimon/table/system/SchemasTable.java
+++ b/paimon-core/src/main/java/org/apache/paimon/table/system/SchemasTable.java
@@ -62,6 +62,7 @@ import 
org.apache.paimon.shade.guava30.com.google.common.collect.Iterators;
 
 import javax.annotation.Nullable;
 
+import java.io.FileNotFoundException;
 import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
@@ -310,13 +311,24 @@ public class SchemasTable implements ReadonlyTable {
 
     private static List<TableSchema> schemasWithId(
             SchemaManager schemaManager, List<Long> schemaIds) {
-        return 
schemaIds.stream().map(schemaManager::schema).collect(Collectors.toList());
+        List<TableSchema> schemas = new ArrayList<>();
+        for (long schemaId : schemaIds) {
+            try {
+                schemas.add(schemaManager.tryGetSchema(schemaId));
+            } catch (FileNotFoundException ignored) {
+            }
+        }
+        return schemas;
     }
 
     private static List<TableSchema> listWithRange(
             SchemaManager schemaManager,
             @Nullable Long optionalMinSchemaId,
             @Nullable Long optionalMaxSchemaId) {
+        if (optionalMinSchemaId != null && 
optionalMinSchemaId.equals(optionalMaxSchemaId)) {
+            return schemasWithId(schemaManager, 
Collections.singletonList(optionalMinSchemaId));
+        }
+
         long lowerBoundSchemaId = 0L;
 
         Optional<TableSchema> latest = schemaManager.latest();
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/utils/SnapshotManager.java 
b/paimon-core/src/main/java/org/apache/paimon/utils/SnapshotManager.java
index 23694934e8..75be8fe921 100644
--- a/paimon-core/src/main/java/org/apache/paimon/utils/SnapshotManager.java
+++ b/paimon-core/src/main/java/org/apache/paimon/utils/SnapshotManager.java
@@ -550,14 +550,24 @@ public class SnapshotManager implements Serializable {
     }
 
     public Iterator<Snapshot> snapshotsWithId(List<Long> snapshotIds) {
-        return snapshotIds.stream()
-                .map(this::snapshot)
-                .sorted(Comparator.comparingLong(Snapshot::id))
-                .iterator();
+        List<Snapshot> snapshots = new ArrayList<>();
+        for (long snapshotId : snapshotIds) {
+            try {
+                snapshots.add(tryGetSnapshot(snapshotId));
+            } catch (FileNotFoundException ignored) {
+            }
+        }
+        snapshots.sort(Comparator.comparingLong(Snapshot::id));
+        return snapshots.iterator();
     }
 
     public Iterator<Snapshot> snapshotsWithinRange(
             Optional<Long> optionalMaxSnapshotId, Optional<Long> 
optionalMinSnapshotId) {
+        if (optionalMaxSnapshotId.isPresent()
+                && optionalMaxSnapshotId.equals(optionalMinSnapshotId)) {
+            return 
snapshotsWithId(Collections.singletonList(optionalMaxSnapshotId.get()));
+        }
+
         Long lowerBoundSnapshotId = earliestSnapshotId();
         Long upperBoundSnapshotId = latestSnapshotId();
         Long lowerId;
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/table/system/SchemasTableTest.java
 
b/paimon-core/src/test/java/org/apache/paimon/table/system/SchemasTableTest.java
index c7c0ca5cf8..36bf05788c 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/table/system/SchemasTableTest.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/table/system/SchemasTableTest.java
@@ -24,13 +24,17 @@ import org.apache.paimon.data.BinaryString;
 import org.apache.paimon.data.GenericRow;
 import org.apache.paimon.data.InternalRow;
 import org.apache.paimon.data.Timestamp;
+import org.apache.paimon.data.serializer.InternalRowSerializer;
 import org.apache.paimon.fs.FileIO;
 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.schema.Schema;
 import org.apache.paimon.schema.SchemaManager;
 import org.apache.paimon.schema.TableSchema;
 import org.apache.paimon.table.TableTestBase;
+import org.apache.paimon.table.source.ReadBuilder;
 import org.apache.paimon.types.DataTypes;
 
 import org.junit.jupiter.api.BeforeEach;
@@ -40,6 +44,7 @@ import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 import static org.apache.paimon.utils.JsonSerdeUtil.toFlatJson;
@@ -78,6 +83,41 @@ public class SchemasTableTest extends TableTestBase {
         assertThat(result).containsExactlyElementsOf(expectRow);
     }
 
+    @Test
+    public void testReadSchemasWithInFilterContainingUnknownId() throws 
Exception {
+        PredicateBuilder builder = new 
PredicateBuilder(schemasTable.rowType());
+        Predicate predicate =
+                builder.in(
+                        
schemasTable.rowType().getFieldNames().indexOf("schema_id"),
+                        Arrays.asList(0L, 99L));
+
+        ReadBuilder readBuilder = 
schemasTable.newReadBuilder().withFilter(predicate);
+        List<InternalRow> result = new ArrayList<>();
+        InternalRowSerializer serializer = new 
InternalRowSerializer(schemasTable.rowType());
+        readBuilder
+                .newRead()
+                .createReader(readBuilder.newScan().plan())
+                .forEachRemaining(row -> result.add(serializer.copy(row)));
+
+        assertThat(result).containsExactlyElementsOf(getExpectedResult());
+    }
+
+    @Test
+    public void testReadSchemasWithEqualFilterOnUnknownId() throws Exception {
+        PredicateBuilder builder = new 
PredicateBuilder(schemasTable.rowType());
+        Predicate predicate =
+                
builder.equal(schemasTable.rowType().getFieldNames().indexOf("schema_id"), 99L);
+
+        ReadBuilder readBuilder = 
schemasTable.newReadBuilder().withFilter(predicate);
+        List<InternalRow> result = new ArrayList<>();
+        readBuilder
+                .newRead()
+                .createReader(readBuilder.newScan().plan())
+                .forEachRemaining(result::add);
+
+        assertThat(result).isEmpty();
+    }
+
     private List<InternalRow> getExpectedResult() {
         List<TableSchema> tableSchemas = schemaManager.listAll();
 
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/table/system/SnapshotsTableTest.java
 
b/paimon-core/src/test/java/org/apache/paimon/table/system/SnapshotsTableTest.java
index c1a6b6f714..34d634e642 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/table/system/SnapshotsTableTest.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/table/system/SnapshotsTableTest.java
@@ -25,9 +25,12 @@ import org.apache.paimon.data.BinaryString;
 import org.apache.paimon.data.GenericRow;
 import org.apache.paimon.data.InternalRow;
 import org.apache.paimon.data.Timestamp;
+import org.apache.paimon.data.serializer.InternalRowSerializer;
 import org.apache.paimon.fs.FileIO;
 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.schema.Schema;
 import org.apache.paimon.schema.SchemaManager;
 import org.apache.paimon.schema.SchemaUtils;
@@ -35,6 +38,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,6 +49,7 @@ import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 import static org.apache.paimon.SnapshotTest.newSnapshotManager;
@@ -96,6 +101,41 @@ public class SnapshotsTableTest extends TableTestBase {
         assertThat(result).containsExactlyInAnyOrderElementsOf(expectedRow);
     }
 
+    @Test
+    public void testReadSnapshotsWithInFilterContainingUnknownId() throws 
Exception {
+        PredicateBuilder builder = new 
PredicateBuilder(snapshotsTable.rowType());
+        Predicate predicate =
+                builder.in(
+                        
snapshotsTable.rowType().getFieldNames().indexOf("snapshot_id"),
+                        Arrays.asList(1L, 99L));
+
+        ReadBuilder readBuilder = 
snapshotsTable.newReadBuilder().withFilter(predicate);
+        List<InternalRow> result = new ArrayList<>();
+        InternalRowSerializer serializer = new 
InternalRowSerializer(snapshotsTable.rowType());
+        readBuilder
+                .newRead()
+                .createReader(readBuilder.newScan().plan())
+                .forEachRemaining(row -> result.add(serializer.copy(row)));
+
+        
assertThat(result).containsExactlyInAnyOrderElementsOf(getExpectedResult(new 
long[] {1}));
+    }
+
+    @Test
+    public void testReadSnapshotsWithEqualFilterOnUnknownId() throws Exception 
{
+        PredicateBuilder builder = new 
PredicateBuilder(snapshotsTable.rowType());
+        Predicate predicate =
+                
builder.equal(snapshotsTable.rowType().getFieldNames().indexOf("snapshot_id"), 
99L);
+
+        ReadBuilder readBuilder = 
snapshotsTable.newReadBuilder().withFilter(predicate);
+        List<InternalRow> result = new ArrayList<>();
+        readBuilder
+                .newRead()
+                .createReader(readBuilder.newScan().plan())
+                .forEachRemaining(result::add);
+
+        assertThat(result).isEmpty();
+    }
+
     private List<InternalRow> getExpectedResult(long[] snapshotIds) {
         List<InternalRow> expectedRow = new ArrayList<>();
         for (long snapshotId : snapshotIds) {
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/utils/SnapshotManagerTest.java 
b/paimon-core/src/test/java/org/apache/paimon/utils/SnapshotManagerTest.java
index 6c09b9a327..2750feb902 100644
--- a/paimon-core/src/test/java/org/apache/paimon/utils/SnapshotManagerTest.java
+++ b/paimon-core/src/test/java/org/apache/paimon/utils/SnapshotManagerTest.java
@@ -36,8 +36,10 @@ import org.mockito.Mockito;
 import javax.annotation.Nullable;
 
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -81,6 +83,18 @@ public class SnapshotManagerTest {
         }
     }
 
+    @Test
+    public void testSnapshotsWithIdSkipsExpiredSnapshot() throws Exception {
+        FileIO fileIO = Mockito.mock(FileIO.class);
+        Mockito.when(fileIO.exists(Mockito.any(Path.class))).thenReturn(true);
+        Mockito.when(fileIO.readFileUtf8(Mockito.any(Path.class)))
+                .thenThrow(new FileNotFoundException());
+        SnapshotManager snapshotManager = newSnapshotManager(fileIO, new 
Path(tempDir.toString()));
+
+        
assertThat(snapshotManager.snapshotsWithId(Collections.singletonList(1L)).hasNext())
+                .isFalse();
+    }
+
     @ParameterizedTest
     @ValueSource(booleans = {true, false})
     public void testEarliestSnapshot(boolean isRaceCondition) throws 
IOException {
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 3dc809c5ef..9024edeca5 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
@@ -379,13 +379,11 @@ public class CatalogTableITCase extends CatalogITCaseBase 
{
                                 + "\"manifest.compression\":\"snappy\"}, ]]");
 
         // check with not exist schema id
-        assertThatThrownBy(
-                        () ->
-                                sql(
-                                        "SELECT schema_id, fields, 
partition_keys, "
-                                                + "primary_keys, options, 
`comment` FROM T$schemas where schema_id = 5"))
-                .hasCauseInstanceOf(RuntimeException.class)
-                .hasRootCauseMessage("schema id: 5 should not greater than max 
schema id: 4");
+        assertThat(
+                        sql(
+                                "SELECT schema_id, fields, partition_keys, "
+                                        + "primary_keys, options, `comment` 
FROM T$schemas where schema_id = 5"))
+                .isEmpty();
 
         // check with not exist schema id
         assertThatThrownBy(
diff --git 
a/paimon-hive/paimon-hive-connector-common/src/test/java/org/apache/paimon/hive/FlinkGenericCatalogITCase.java
 
b/paimon-hive/paimon-hive-connector-common/src/test/java/org/apache/paimon/hive/FlinkGenericCatalogITCase.java
index 0ac539224c..b48404fb1c 100644
--- 
a/paimon-hive/paimon-hive-connector-common/src/test/java/org/apache/paimon/hive/FlinkGenericCatalogITCase.java
+++ 
b/paimon-hive/paimon-hive-connector-common/src/test/java/org/apache/paimon/hive/FlinkGenericCatalogITCase.java
@@ -163,13 +163,10 @@ public class FlinkGenericCatalogITCase extends 
AbstractTestBaseJUnit4 {
         assertThat(result2).containsExactly(Row.of(2L, 0L, "APPEND"));
 
         // check leaf predicate query with exist snapshot_id
-        assertThatThrownBy(
-                        () ->
-                                sql(
-                                        "SELECT snapshot_id, schema_id, 
commit_kind FROM paimon_t$snapshots where snapshot_id=6"))
-                .hasCauseInstanceOf(RuntimeException.class)
-                .hasRootCauseMessage(
-                        "snapshot upper id:6 should not greater than 
latestSnapshotId:4");
+        assertThat(
+                        sql(
+                                "SELECT snapshot_id, schema_id, commit_kind 
FROM paimon_t$snapshots where snapshot_id=6"))
+                .isEmpty();
 
         // check compound predicate query with right range
         List<Row> result3 =

Reply via email to