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

aokolnychyi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg.git


The following commit(s) were added to refs/heads/main by this push:
     new 6a41168d1a Core: Avoid exceptions for accessing schema for metadata 
tables in SnapshotUtil (#15387)
6a41168d1a is described below

commit 6a41168d1aede8597eff057d91681ccd0eafbec1
Author: Anton Okolnychyi <[email protected]>
AuthorDate: Fri Feb 20 14:33:02 2026 -0800

    Core: Avoid exceptions for accessing schema for metadata tables in 
SnapshotUtil (#15387)
---
 .../java/org/apache/iceberg/util/SnapshotUtil.java |  8 +++
 .../org/apache/iceberg/util/TestSnapshotUtil.java  | 69 ++++++++++++++++++++++
 2 files changed, 77 insertions(+)

diff --git a/core/src/main/java/org/apache/iceberg/util/SnapshotUtil.java 
b/core/src/main/java/org/apache/iceberg/util/SnapshotUtil.java
index 3c6911abef..dccc23c2e2 100644
--- a/core/src/main/java/org/apache/iceberg/util/SnapshotUtil.java
+++ b/core/src/main/java/org/apache/iceberg/util/SnapshotUtil.java
@@ -23,6 +23,7 @@ import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.function.Function;
+import org.apache.iceberg.BaseMetadataTable;
 import org.apache.iceberg.DataFile;
 import org.apache.iceberg.HistoryEntry;
 import org.apache.iceberg.Schema;
@@ -395,11 +396,18 @@ public class SnapshotUtil {
   /**
    * Returns the schema of the table for the specified snapshot.
    *
+   * <p>Note that metadata tables may support time travel but don't inherit 
the snapshot schema,
+   * unlike normal data scans.
+   *
    * @param table a {@link Table}
    * @param snapshotId the ID of the snapshot
    * @return the schema
    */
   public static Schema schemaFor(Table table, long snapshotId) {
+    if (table instanceof BaseMetadataTable) {
+      return table.schema();
+    }
+
     Snapshot snapshot = table.snapshot(snapshotId);
     Preconditions.checkArgument(snapshot != null, "Cannot find snapshot with 
ID %s", snapshotId);
     Integer schemaId = snapshot.schemaId();
diff --git a/core/src/test/java/org/apache/iceberg/util/TestSnapshotUtil.java 
b/core/src/test/java/org/apache/iceberg/util/TestSnapshotUtil.java
index 37d0953ab8..96b56e5ffb 100644
--- a/core/src/test/java/org/apache/iceberg/util/TestSnapshotUtil.java
+++ b/core/src/test/java/org/apache/iceberg/util/TestSnapshotUtil.java
@@ -21,6 +21,7 @@ package org.apache.iceberg.util;
 import static org.apache.iceberg.types.Types.NestedField.optional;
 import static org.apache.iceberg.types.Types.NestedField.required;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import java.io.File;
 import java.util.Iterator;
@@ -28,10 +29,13 @@ import java.util.List;
 import java.util.stream.StreamSupport;
 import org.apache.iceberg.DataFile;
 import org.apache.iceberg.DataFiles;
+import org.apache.iceberg.MetadataTableType;
+import org.apache.iceberg.MetadataTableUtils;
 import org.apache.iceberg.PartitionSpec;
 import org.apache.iceberg.Schema;
 import org.apache.iceberg.Snapshot;
 import org.apache.iceberg.SnapshotRef;
+import org.apache.iceberg.Table;
 import org.apache.iceberg.TestHelpers;
 import org.apache.iceberg.TestTables;
 import org.apache.iceberg.types.Types;
@@ -248,4 +252,69 @@ public class TestSnapshotUtil {
     assertThat(table.schema().asStruct()).isEqualTo(expected.asStruct());
     assertThat(SnapshotUtil.schemaFor(table, 
tag).asStruct()).isEqualTo(initialSchema.asStruct());
   }
+
+  @Test
+  public void schemaForSnapshotId() {
+    Schema initialSchema =
+        new Schema(
+            required(1, "id", Types.IntegerType.get()),
+            required(2, "data", Types.StringType.get()));
+    assertThat(table.schema().asStruct()).isEqualTo(initialSchema.asStruct());
+
+    long firstSnapshotId = table.currentSnapshot().snapshotId();
+    assertThat(SnapshotUtil.schemaFor(table, firstSnapshotId).asStruct())
+        .isEqualTo(initialSchema.asStruct());
+
+    table.updateSchema().addColumn("zip", Types.IntegerType.get()).commit();
+    appendFileToMain();
+
+    Schema updatedSchema =
+        new Schema(
+            required(1, "id", Types.IntegerType.get()),
+            required(2, "data", Types.StringType.get()),
+            optional(3, "zip", Types.IntegerType.get()));
+
+    long secondSnapshotId = table.currentSnapshot().snapshotId();
+
+    assertThat(SnapshotUtil.schemaFor(table, firstSnapshotId).asStruct())
+        .isEqualTo(initialSchema.asStruct());
+    assertThat(SnapshotUtil.schemaFor(table, secondSnapshotId).asStruct())
+        .isEqualTo(updatedSchema.asStruct());
+  }
+
+  @Test
+  public void schemaForSnapshotIdInvalidSnapshot() {
+    long invalidSnapshotId = 999999L;
+    assertThat(table.snapshot(invalidSnapshotId)).isNull();
+
+    assertThatThrownBy(() -> SnapshotUtil.schemaFor(table, invalidSnapshotId))
+        .isInstanceOf(IllegalArgumentException.class)
+        .hasMessageContaining("Cannot find snapshot with ID " + 
invalidSnapshotId);
+  }
+
+  @Test
+  public void schemaForSnapshotIdMetadataTable() {
+    long firstSnapshotId = table.currentSnapshot().snapshotId();
+
+    table.updateSchema().addColumn("zip", Types.IntegerType.get()).commit();
+    appendFileToMain();
+
+    long secondSnapshotId = table.currentSnapshot().snapshotId();
+
+    Schema updatedSchema =
+        new Schema(
+            required(1, "id", Types.IntegerType.get()),
+            required(2, "data", Types.StringType.get()),
+            optional(3, "zip", Types.IntegerType.get()));
+
+    assertThat(table.schema().asStruct()).isEqualTo(updatedSchema.asStruct());
+
+    Table snapshotsTable =
+        MetadataTableUtils.createMetadataTableInstance(table, 
MetadataTableType.SNAPSHOTS);
+
+    assertThat(SnapshotUtil.schemaFor(snapshotsTable, 
secondSnapshotId).asStruct())
+        .isEqualTo(snapshotsTable.schema().asStruct());
+    assertThat(SnapshotUtil.schemaFor(snapshotsTable, 
firstSnapshotId).asStruct())
+        .isEqualTo(snapshotsTable.schema().asStruct());
+  }
 }

Reply via email to