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

stevenzwu 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 01cf810cd0 Core: Add modified file and row counts to ManifestInfo 
(#18212)
01cf810cd0 is described below

commit 01cf810cd096fb5ff94e165a8bcb5ae161e7aea8
Author: Steven Zhen Wu <[email protected]>
AuthorDate: Mon Sep 28 16:31:37 2026 -0700

    Core: Add modified file and row counts to ManifestInfo (#18212)
    
    * Core: Add modified file and row counts to ManifestInfo
    
    The v4 AMT entry-structure spec gives MODIFIED its own
    modified_files_count (524) and modified_rows_count (525) fields.
---
 .../main/java/org/apache/iceberg/ManifestInfo.java |  18 ++-
 .../org/apache/iceberg/ManifestInfoStruct.java     |  79 ++++++++++--
 .../org/apache/iceberg/TestManifestInfoStruct.java | 137 ++++++++++++++++++++-
 .../apache/iceberg/TestTrackedFileAdapters.java    |   2 +
 .../org/apache/iceberg/TestV4ManifestReader.java   |   2 +-
 .../java/org/apache/iceberg/V4TestComparators.java |   2 +
 6 files changed, 225 insertions(+), 15 deletions(-)

diff --git a/core/src/main/java/org/apache/iceberg/ManifestInfo.java 
b/core/src/main/java/org/apache/iceberg/ManifestInfo.java
index 04351e2afd..c41a008526 100644
--- a/core/src/main/java/org/apache/iceberg/ManifestInfo.java
+++ b/core/src/main/java/org/apache/iceberg/ManifestInfo.java
@@ -33,7 +33,10 @@ interface ManifestInfo {
           506, "deleted_files_count", Types.IntegerType.get(), "Number of 
deleted files");
   Types.NestedField REPLACED_FILES_COUNT =
       Types.NestedField.required(
-          520, "replaced_files_count", Types.IntegerType.get(), "Number of 
replaced files");
+          523, "replaced_files_count", Types.IntegerType.get(), "Number of 
replaced files");
+  Types.NestedField MODIFIED_FILES_COUNT =
+      Types.NestedField.required(
+          525, "modified_files_count", Types.IntegerType.get(), "Number of 
modified files");
   Types.NestedField ADDED_ROWS_COUNT =
       Types.NestedField.required(
           512, "added_rows_count", Types.LongType.get(), "Number of rows in 
added files");
@@ -45,7 +48,10 @@ interface ManifestInfo {
           514, "deleted_rows_count", Types.LongType.get(), "Number of rows in 
deleted files");
   Types.NestedField REPLACED_ROWS_COUNT =
       Types.NestedField.required(
-          521, "replaced_rows_count", Types.LongType.get(), "Number of rows in 
replaced files");
+          524, "replaced_rows_count", Types.LongType.get(), "Number of rows in 
replaced files");
+  Types.NestedField MODIFIED_ROWS_COUNT =
+      Types.NestedField.required(
+          526, "modified_rows_count", Types.LongType.get(), "Number of rows in 
modified files");
   Types.NestedField MIN_SEQUENCE_NUMBER =
       Types.NestedField.required(
           516,
@@ -62,10 +68,12 @@ interface ManifestInfo {
         EXISTING_FILES_COUNT,
         DELETED_FILES_COUNT,
         REPLACED_FILES_COUNT,
+        MODIFIED_FILES_COUNT,
         ADDED_ROWS_COUNT,
         EXISTING_ROWS_COUNT,
         DELETED_ROWS_COUNT,
         REPLACED_ROWS_COUNT,
+        MODIFIED_ROWS_COUNT,
         MIN_SEQUENCE_NUMBER,
         DV);
   }
@@ -82,6 +90,9 @@ interface ManifestInfo {
   /** Returns the number of replaced files in this manifest. */
   int replacedFilesCount();
 
+  /** Returns the number of modified files in this manifest. */
+  int modifiedFilesCount();
+
   /** Returns the number of rows in added files. */
   long addedRowsCount();
 
@@ -94,6 +105,9 @@ interface ManifestInfo {
   /** Returns the number of rows in replaced files. */
   long replacedRowsCount();
 
+  /** Returns the number of rows in modified files. */
+  long modifiedRowsCount();
+
   /** Returns the minimum sequence number of files in this manifest. */
   long minSequenceNumber();
 
diff --git a/core/src/main/java/org/apache/iceberg/ManifestInfoStruct.java 
b/core/src/main/java/org/apache/iceberg/ManifestInfoStruct.java
index b37c3edf7f..26a2fcef76 100644
--- a/core/src/main/java/org/apache/iceberg/ManifestInfoStruct.java
+++ b/core/src/main/java/org/apache/iceberg/ManifestInfoStruct.java
@@ -36,10 +36,12 @@ class ManifestInfoStruct extends SupportsIndexProjection 
implements ManifestInfo
           ManifestInfo.EXISTING_FILES_COUNT,
           ManifestInfo.DELETED_FILES_COUNT,
           ManifestInfo.REPLACED_FILES_COUNT,
+          ManifestInfo.MODIFIED_FILES_COUNT,
           ManifestInfo.ADDED_ROWS_COUNT,
           ManifestInfo.EXISTING_ROWS_COUNT,
           ManifestInfo.DELETED_ROWS_COUNT,
           ManifestInfo.REPLACED_ROWS_COUNT,
+          ManifestInfo.MODIFIED_ROWS_COUNT,
           ManifestInfo.MIN_SEQUENCE_NUMBER,
           ManifestInfo.DV);
 
@@ -47,10 +49,12 @@ class ManifestInfoStruct extends SupportsIndexProjection 
implements ManifestInfo
   private int existingFilesCount = -1;
   private int deletedFilesCount = -1;
   private int replacedFilesCount = -1;
+  private int modifiedFilesCount = -1;
   private long addedRowsCount = -1L;
   private long existingRowsCount = -1L;
   private long deletedRowsCount = -1L;
   private long replacedRowsCount = -1L;
+  private long modifiedRowsCount = -1L;
   private long minSequenceNumber = -1L;
   private byte[] dv = null;
 
@@ -66,10 +70,12 @@ class ManifestInfoStruct extends SupportsIndexProjection 
implements ManifestInfo
     this.existingFilesCount = toCopy.existingFilesCount;
     this.deletedFilesCount = toCopy.deletedFilesCount;
     this.replacedFilesCount = toCopy.replacedFilesCount;
+    this.modifiedFilesCount = toCopy.modifiedFilesCount;
     this.addedRowsCount = toCopy.addedRowsCount;
     this.existingRowsCount = toCopy.existingRowsCount;
     this.deletedRowsCount = toCopy.deletedRowsCount;
     this.replacedRowsCount = toCopy.replacedRowsCount;
+    this.modifiedRowsCount = toCopy.modifiedRowsCount;
     this.minSequenceNumber = toCopy.minSequenceNumber;
     this.dv = toCopy.dv != null ? Arrays.copyOf(toCopy.dv, toCopy.dv.length) : 
null;
   }
@@ -79,10 +85,12 @@ class ManifestInfoStruct extends SupportsIndexProjection 
implements ManifestInfo
       int existingFilesCount,
       int deletedFilesCount,
       int replacedFilesCount,
+      int modifiedFilesCount,
       long addedRowsCount,
       long existingRowsCount,
       long deletedRowsCount,
       long replacedRowsCount,
+      long modifiedRowsCount,
       long minSequenceNumber,
       byte[] dv) {
     super(BASE_TYPE.fields().size());
@@ -90,10 +98,12 @@ class ManifestInfoStruct extends SupportsIndexProjection 
implements ManifestInfo
     this.existingFilesCount = existingFilesCount;
     this.deletedFilesCount = deletedFilesCount;
     this.replacedFilesCount = replacedFilesCount;
+    this.modifiedFilesCount = modifiedFilesCount;
     this.addedRowsCount = addedRowsCount;
     this.existingRowsCount = existingRowsCount;
     this.deletedRowsCount = deletedRowsCount;
     this.replacedRowsCount = replacedRowsCount;
+    this.modifiedRowsCount = modifiedRowsCount;
     this.minSequenceNumber = minSequenceNumber;
     this.dv = dv;
   }
@@ -118,6 +128,11 @@ class ManifestInfoStruct extends SupportsIndexProjection 
implements ManifestInfo
     return replacedFilesCount;
   }
 
+  @Override
+  public int modifiedFilesCount() {
+    return modifiedFilesCount;
+  }
+
   @Override
   public long addedRowsCount() {
     return addedRowsCount;
@@ -138,6 +153,11 @@ class ManifestInfoStruct extends SupportsIndexProjection 
implements ManifestInfo
     return replacedRowsCount;
   }
 
+  @Override
+  public long modifiedRowsCount() {
+    return modifiedRowsCount;
+  }
+
   @Override
   public long minSequenceNumber() {
     return minSequenceNumber;
@@ -173,16 +193,20 @@ class ManifestInfoStruct extends SupportsIndexProjection 
implements ManifestInfo
       case 3:
         return replacedFilesCount;
       case 4:
-        return addedRowsCount;
+        return modifiedFilesCount;
       case 5:
-        return existingRowsCount;
+        return addedRowsCount;
       case 6:
-        return deletedRowsCount;
+        return existingRowsCount;
       case 7:
-        return replacedRowsCount;
+        return deletedRowsCount;
       case 8:
-        return minSequenceNumber;
+        return replacedRowsCount;
       case 9:
+        return modifiedRowsCount;
+      case 10:
+        return minSequenceNumber;
+      case 11:
         return dv != null ? ByteBuffer.wrap(dv) : null;
       default:
         throw new UnsupportedOperationException("Unknown field ordinal: " + 
pos);
@@ -205,21 +229,27 @@ class ManifestInfoStruct extends SupportsIndexProjection 
implements ManifestInfo
         this.replacedFilesCount = (Integer) value;
         break;
       case 4:
-        this.addedRowsCount = (Long) value;
+        this.modifiedFilesCount = (Integer) value;
         break;
       case 5:
-        this.existingRowsCount = (Long) value;
+        this.addedRowsCount = (Long) value;
         break;
       case 6:
-        this.deletedRowsCount = (Long) value;
+        this.existingRowsCount = (Long) value;
         break;
       case 7:
-        this.replacedRowsCount = (Long) value;
+        this.deletedRowsCount = (Long) value;
         break;
       case 8:
-        this.minSequenceNumber = (Long) value;
+        this.replacedRowsCount = (Long) value;
         break;
       case 9:
+        this.modifiedRowsCount = (Long) value;
+        break;
+      case 10:
+        this.minSequenceNumber = (Long) value;
+        break;
+      case 11:
         this.dv = ByteBuffers.toByteArray((ByteBuffer) value);
         this.mdv = null;
         break;
@@ -239,10 +269,12 @@ class ManifestInfoStruct extends SupportsIndexProjection 
implements ManifestInfo
         .add("existing_files_count", existingFilesCount)
         .add("deleted_files_count", deletedFilesCount)
         .add("replaced_files_count", replacedFilesCount)
+        .add("modified_files_count", modifiedFilesCount)
         .add("added_rows_count", addedRowsCount)
         .add("existing_rows_count", existingRowsCount)
         .add("deleted_rows_count", deletedRowsCount)
         .add("replaced_rows_count", replacedRowsCount)
+        .add("modified_rows_count", modifiedRowsCount)
         .add("min_sequence_number", minSequenceNumber)
         .add("dv", dv == null ? "null" : "(binary)")
         .toString();
@@ -253,10 +285,12 @@ class ManifestInfoStruct extends SupportsIndexProjection 
implements ManifestInfo
     private Integer existingFilesCount = null;
     private Integer deletedFilesCount = null;
     private Integer replacedFilesCount = null;
+    private Integer modifiedFilesCount = null;
     private Long addedRowsCount = null;
     private Long existingRowsCount = null;
     private Long deletedRowsCount = null;
     private Long replacedRowsCount = null;
+    private Long modifiedRowsCount = null;
     private Long minSequenceNumber = null;
     private byte[] dv = null;
 
@@ -288,6 +322,13 @@ class ManifestInfoStruct extends SupportsIndexProjection 
implements ManifestInfo
       return this;
     }
 
+    Builder modifiedFilesCount(int count) {
+      Preconditions.checkArgument(
+          count >= 0, "Invalid modified files count: %s (must be >= 0)", 
count);
+      this.modifiedFilesCount = count;
+      return this;
+    }
+
     Builder addedRowsCount(long count) {
       Preconditions.checkArgument(count >= 0, "Invalid added rows count: %s 
(must be >= 0)", count);
       this.addedRowsCount = count;
@@ -315,6 +356,13 @@ class ManifestInfoStruct extends SupportsIndexProjection 
implements ManifestInfo
       return this;
     }
 
+    Builder modifiedRowsCount(long count) {
+      Preconditions.checkArgument(
+          count >= 0, "Invalid modified rows count: %s (must be >= 0)", count);
+      this.modifiedRowsCount = count;
+      return this;
+    }
+
     Builder minSequenceNumber(long sequenceNumber) {
       Preconditions.checkArgument(
           sequenceNumber >= 0, "Invalid min sequence number: %s (must be >= 
0)", sequenceNumber);
@@ -337,6 +385,8 @@ class ManifestInfoStruct extends SupportsIndexProjection 
implements ManifestInfo
           deletedFilesCount != null, "Missing required value: deleted files 
count");
       Preconditions.checkArgument(
           replacedFilesCount != null, "Missing required value: replaced files 
count");
+      Preconditions.checkArgument(
+          modifiedFilesCount != null, "Missing required value: modified files 
count");
       Preconditions.checkArgument(
           addedRowsCount != null, "Missing required value: added rows count");
       Preconditions.checkArgument(
@@ -345,6 +395,8 @@ class ManifestInfoStruct extends SupportsIndexProjection 
implements ManifestInfo
           deletedRowsCount != null, "Missing required value: deleted rows 
count");
       Preconditions.checkArgument(
           replacedRowsCount != null, "Missing required value: replaced rows 
count");
+      Preconditions.checkArgument(
+          modifiedRowsCount != null, "Missing required value: modified rows 
count");
       Preconditions.checkArgument(
           minSequenceNumber != null, "Missing required value: min sequence 
number");
       Preconditions.checkArgument(
@@ -367,15 +419,22 @@ class ManifestInfoStruct extends SupportsIndexProjection 
implements ManifestInfo
           "Invalid replaced counts: %s rows in %s files",
           replacedRowsCount,
           replacedFilesCount);
+      Preconditions.checkArgument(
+          modifiedRowsCount == 0 || modifiedFilesCount > 0,
+          "Invalid modified counts: %s rows in %s files",
+          modifiedRowsCount,
+          modifiedFilesCount);
       return new ManifestInfoStruct(
           addedFilesCount,
           existingFilesCount,
           deletedFilesCount,
           replacedFilesCount,
+          modifiedFilesCount,
           addedRowsCount,
           existingRowsCount,
           deletedRowsCount,
           replacedRowsCount,
+          modifiedRowsCount,
           minSequenceNumber,
           dv);
     }
diff --git a/core/src/test/java/org/apache/iceberg/TestManifestInfoStruct.java 
b/core/src/test/java/org/apache/iceberg/TestManifestInfoStruct.java
index cc9710db04..1892f2d890 100644
--- a/core/src/test/java/org/apache/iceberg/TestManifestInfoStruct.java
+++ b/core/src/test/java/org/apache/iceberg/TestManifestInfoStruct.java
@@ -33,16 +33,29 @@ class TestManifestInfoStruct {
   void fieldAccess() {
     ManifestInfoStruct info =
         new ManifestInfoStruct(
-            10, 20, 3, 2, 1000L, 2000L, 300L, 200L, 5L, 
MumblingTestUtil.onlyFirstBitSetBytes());
+            10,
+            20,
+            3,
+            2,
+            1,
+            1000L,
+            2000L,
+            300L,
+            200L,
+            100L,
+            5L,
+            MumblingTestUtil.onlyFirstBitSetBytes());
 
     assertThat(info.addedFilesCount()).isEqualTo(10);
     assertThat(info.existingFilesCount()).isEqualTo(20);
     assertThat(info.deletedFilesCount()).isEqualTo(3);
     assertThat(info.replacedFilesCount()).isEqualTo(2);
+    assertThat(info.modifiedFilesCount()).isEqualTo(1);
     assertThat(info.addedRowsCount()).isEqualTo(1000L);
     assertThat(info.existingRowsCount()).isEqualTo(2000L);
     assertThat(info.deletedRowsCount()).isEqualTo(300L);
     assertThat(info.replacedRowsCount()).isEqualTo(200L);
+    assertThat(info.modifiedRowsCount()).isEqualTo(100L);
     assertThat(info.minSequenceNumber()).isEqualTo(5L);
     assertThat(info.manifestDeletionVector().buffer())
         .isEqualTo(ByteBuffer.wrap(MumblingTestUtil.onlyFirstBitSetBytes()));
@@ -57,10 +70,12 @@ class TestManifestInfoStruct {
             .existingFilesCount(20)
             .deletedFilesCount(3)
             .replacedFilesCount(2)
+            .modifiedFilesCount(1)
             .addedRowsCount(1000L)
             .existingRowsCount(2000L)
             .deletedRowsCount(300L)
             .replacedRowsCount(200L)
+            .modifiedRowsCount(100L)
             .minSequenceNumber(5L)
             .dv(ByteBuffer.wrap(MumblingTestUtil.onlyFirstBitSetBytes()))
             .build();
@@ -71,10 +86,12 @@ class TestManifestInfoStruct {
     assertThat(copy.existingFilesCount()).isEqualTo(20);
     assertThat(copy.deletedFilesCount()).isEqualTo(3);
     assertThat(copy.replacedFilesCount()).isEqualTo(2);
+    assertThat(copy.modifiedFilesCount()).isEqualTo(1);
     assertThat(copy.addedRowsCount()).isEqualTo(1000L);
     assertThat(copy.existingRowsCount()).isEqualTo(2000L);
     assertThat(copy.deletedRowsCount()).isEqualTo(300L);
     assertThat(copy.replacedRowsCount()).isEqualTo(200L);
+    assertThat(copy.modifiedRowsCount()).isEqualTo(100L);
     assertThat(copy.minSequenceNumber()).isEqualTo(5L);
 
     // verify deep copy of dv byte array
@@ -90,10 +107,12 @@ class TestManifestInfoStruct {
             .existingFilesCount(0)
             .deletedFilesCount(0)
             .replacedFilesCount(0)
+            .modifiedFilesCount(0)
             .addedRowsCount(0L)
             .existingRowsCount(0L)
             .deletedRowsCount(0L)
             .replacedRowsCount(0L)
+            .modifiedRowsCount(0L)
             .minSequenceNumber(0L)
             .build();
 
@@ -110,7 +129,7 @@ class TestManifestInfoStruct {
     assertThat(info.size()).isEqualTo(2);
 
     // projected position 0 maps to internal position 0 (added_files_count)
-    // projected position 1 maps to internal position 8 (min_sequence_number)
+    // projected position 1 maps to internal position 10 (min_sequence_number)
     info.set(0, 10);
     info.set(1, 5L);
 
@@ -128,10 +147,12 @@ class TestManifestInfoStruct {
             .existingFilesCount(20)
             .deletedFilesCount(3)
             .replacedFilesCount(2)
+            .modifiedFilesCount(1)
             .addedRowsCount(1000L)
             .existingRowsCount(2000L)
             .deletedRowsCount(300L)
             .replacedRowsCount(200L)
+            .modifiedRowsCount(100L)
             .minSequenceNumber(5L)
             .dv(ByteBuffer.wrap(MumblingTestUtil.onlyFirstBitSetBytes()))
             .build();
@@ -145,10 +166,12 @@ class TestManifestInfoStruct {
     assertThat(info.existingFilesCount()).isEqualTo(20);
     assertThat(info.deletedFilesCount()).isEqualTo(3);
     assertThat(info.replacedFilesCount()).isEqualTo(2);
+    assertThat(info.modifiedFilesCount()).isEqualTo(1);
     assertThat(info.addedRowsCount()).isEqualTo(1000L);
     assertThat(info.existingRowsCount()).isEqualTo(2000L);
     assertThat(info.deletedRowsCount()).isEqualTo(300L);
     assertThat(info.replacedRowsCount()).isEqualTo(200L);
+    assertThat(info.modifiedRowsCount()).isEqualTo(100L);
     assertThat(info.minSequenceNumber()).isEqualTo(5L);
     assertThat(info.manifestDeletionVector()).isSameAs(mdv);
   }
@@ -161,10 +184,12 @@ class TestManifestInfoStruct {
             .existingFilesCount(20)
             .deletedFilesCount(3)
             .replacedFilesCount(2)
+            .modifiedFilesCount(1)
             .addedRowsCount(1000L)
             .existingRowsCount(2000L)
             .deletedRowsCount(300L)
             .replacedRowsCount(200L)
+            .modifiedRowsCount(100L)
             .minSequenceNumber(5L)
             .dv(ByteBuffer.wrap(MumblingTestUtil.onlyFirstBitSetBytes()))
             .build();
@@ -175,10 +200,12 @@ class TestManifestInfoStruct {
     assertThat(deserialized.existingFilesCount()).isEqualTo(20);
     assertThat(deserialized.deletedFilesCount()).isEqualTo(3);
     assertThat(deserialized.replacedFilesCount()).isEqualTo(2);
+    assertThat(deserialized.modifiedFilesCount()).isEqualTo(1);
     assertThat(deserialized.addedRowsCount()).isEqualTo(1000L);
     assertThat(deserialized.existingRowsCount()).isEqualTo(2000L);
     assertThat(deserialized.deletedRowsCount()).isEqualTo(300L);
     assertThat(deserialized.replacedRowsCount()).isEqualTo(200L);
+    assertThat(deserialized.modifiedRowsCount()).isEqualTo(100L);
     assertThat(deserialized.minSequenceNumber()).isEqualTo(5L);
     assertThat(deserialized.manifestDeletionVector().buffer())
         .isEqualTo(ByteBuffer.wrap(MumblingTestUtil.onlyFirstBitSetBytes()));
@@ -192,10 +219,12 @@ class TestManifestInfoStruct {
                     .existingFilesCount(0)
                     .deletedFilesCount(0)
                     .replacedFilesCount(0)
+                    .modifiedFilesCount(0)
                     .addedRowsCount(0L)
                     .existingRowsCount(0L)
                     .deletedRowsCount(0L)
                     .replacedRowsCount(0L)
+                    .modifiedRowsCount(0L)
                     .minSequenceNumber(0L)
                     .build())
         .isInstanceOf(IllegalArgumentException.class)
@@ -210,10 +239,12 @@ class TestManifestInfoStruct {
                     .addedFilesCount(0)
                     .deletedFilesCount(0)
                     .replacedFilesCount(0)
+                    .modifiedFilesCount(0)
                     .addedRowsCount(0L)
                     .existingRowsCount(0L)
                     .deletedRowsCount(0L)
                     .replacedRowsCount(0L)
+                    .modifiedRowsCount(0L)
                     .minSequenceNumber(0L)
                     .build())
         .isInstanceOf(IllegalArgumentException.class)
@@ -228,10 +259,12 @@ class TestManifestInfoStruct {
                     .addedFilesCount(0)
                     .existingFilesCount(0)
                     .replacedFilesCount(0)
+                    .modifiedFilesCount(0)
                     .addedRowsCount(0L)
                     .existingRowsCount(0L)
                     .deletedRowsCount(0L)
                     .replacedRowsCount(0L)
+                    .modifiedRowsCount(0L)
                     .minSequenceNumber(0L)
                     .build())
         .isInstanceOf(IllegalArgumentException.class)
@@ -246,10 +279,12 @@ class TestManifestInfoStruct {
                     .addedFilesCount(0)
                     .existingFilesCount(0)
                     .deletedFilesCount(0)
+                    .modifiedFilesCount(0)
                     .addedRowsCount(0L)
                     .existingRowsCount(0L)
                     .deletedRowsCount(0L)
                     .replacedRowsCount(0L)
+                    .modifiedRowsCount(0L)
                     .minSequenceNumber(0L)
                     .build())
         .isInstanceOf(IllegalArgumentException.class)
@@ -265,9 +300,11 @@ class TestManifestInfoStruct {
                     .existingFilesCount(0)
                     .deletedFilesCount(0)
                     .replacedFilesCount(0)
+                    .modifiedFilesCount(0)
                     .existingRowsCount(0L)
                     .deletedRowsCount(0L)
                     .replacedRowsCount(0L)
+                    .modifiedRowsCount(0L)
                     .minSequenceNumber(0L)
                     .build())
         .isInstanceOf(IllegalArgumentException.class)
@@ -283,9 +320,11 @@ class TestManifestInfoStruct {
                     .existingFilesCount(0)
                     .deletedFilesCount(0)
                     .replacedFilesCount(0)
+                    .modifiedFilesCount(0)
                     .addedRowsCount(0L)
                     .deletedRowsCount(0L)
                     .replacedRowsCount(0L)
+                    .modifiedRowsCount(0L)
                     .minSequenceNumber(0L)
                     .build())
         .isInstanceOf(IllegalArgumentException.class)
@@ -301,9 +340,11 @@ class TestManifestInfoStruct {
                     .existingFilesCount(0)
                     .deletedFilesCount(0)
                     .replacedFilesCount(0)
+                    .modifiedFilesCount(0)
                     .addedRowsCount(0L)
                     .existingRowsCount(0L)
                     .replacedRowsCount(0L)
+                    .modifiedRowsCount(0L)
                     .minSequenceNumber(0L)
                     .build())
         .isInstanceOf(IllegalArgumentException.class)
@@ -319,15 +360,57 @@ class TestManifestInfoStruct {
                     .existingFilesCount(0)
                     .deletedFilesCount(0)
                     .replacedFilesCount(0)
+                    .modifiedFilesCount(0)
                     .addedRowsCount(0L)
                     .existingRowsCount(0L)
                     .deletedRowsCount(0L)
+                    .modifiedRowsCount(0L)
                     .minSequenceNumber(0L)
                     .build())
         .isInstanceOf(IllegalArgumentException.class)
         .hasMessage("Missing required value: replaced rows count");
   }
 
+  @Test
+  void builderMissingModifiedFilesCount() {
+    assertThatThrownBy(
+            () ->
+                ManifestInfoStruct.builder()
+                    .addedFilesCount(0)
+                    .existingFilesCount(0)
+                    .deletedFilesCount(0)
+                    .replacedFilesCount(0)
+                    .addedRowsCount(0L)
+                    .existingRowsCount(0L)
+                    .deletedRowsCount(0L)
+                    .replacedRowsCount(0L)
+                    .modifiedRowsCount(0L)
+                    .minSequenceNumber(0L)
+                    .build())
+        .isInstanceOf(IllegalArgumentException.class)
+        .hasMessage("Missing required value: modified files count");
+  }
+
+  @Test
+  void builderMissingModifiedRowsCount() {
+    assertThatThrownBy(
+            () ->
+                ManifestInfoStruct.builder()
+                    .addedFilesCount(0)
+                    .existingFilesCount(0)
+                    .deletedFilesCount(0)
+                    .replacedFilesCount(0)
+                    .modifiedFilesCount(0)
+                    .addedRowsCount(0L)
+                    .existingRowsCount(0L)
+                    .deletedRowsCount(0L)
+                    .replacedRowsCount(0L)
+                    .minSequenceNumber(0L)
+                    .build())
+        .isInstanceOf(IllegalArgumentException.class)
+        .hasMessage("Missing required value: modified rows count");
+  }
+
   @Test
   void builderMissingMinSequenceNumber() {
     assertThatThrownBy(
@@ -337,10 +420,12 @@ class TestManifestInfoStruct {
                     .existingFilesCount(0)
                     .deletedFilesCount(0)
                     .replacedFilesCount(0)
+                    .modifiedFilesCount(0)
                     .addedRowsCount(0L)
                     .existingRowsCount(0L)
                     .deletedRowsCount(0L)
                     .replacedRowsCount(0L)
+                    .modifiedRowsCount(0L)
                     .build())
         .isInstanceOf(IllegalArgumentException.class)
         .hasMessage("Missing required value: min sequence number");
@@ -374,6 +459,13 @@ class TestManifestInfoStruct {
         .hasMessage("Invalid replaced files count: -1 (must be >= 0)");
   }
 
+  @Test
+  void builderRejectsNegativeModifiedFilesCount() {
+    assertThatThrownBy(() -> 
ManifestInfoStruct.builder().modifiedFilesCount(-1))
+        .isInstanceOf(IllegalArgumentException.class)
+        .hasMessage("Invalid modified files count: -1 (must be >= 0)");
+  }
+
   @Test
   void builderRejectsNegativeAddedRowsCount() {
     assertThatThrownBy(() -> ManifestInfoStruct.builder().addedRowsCount(-1L))
@@ -402,6 +494,13 @@ class TestManifestInfoStruct {
         .hasMessage("Invalid replaced rows count: -1 (must be >= 0)");
   }
 
+  @Test
+  void builderRejectsNegativeModifiedRowsCount() {
+    assertThatThrownBy(() -> 
ManifestInfoStruct.builder().modifiedRowsCount(-1L))
+        .isInstanceOf(IllegalArgumentException.class)
+        .hasMessage("Invalid modified rows count: -1 (must be >= 0)");
+  }
+
   @Test
   void builderRejectsNegativeMinSequenceNumber() {
     assertThatThrownBy(() -> 
ManifestInfoStruct.builder().minSequenceNumber(-1L))
@@ -418,10 +517,12 @@ class TestManifestInfoStruct {
                     .existingFilesCount(0)
                     .deletedFilesCount(0)
                     .replacedFilesCount(0)
+                    .modifiedFilesCount(0)
                     .addedRowsCount(10L)
                     .existingRowsCount(0L)
                     .deletedRowsCount(0L)
                     .replacedRowsCount(0L)
+                    .modifiedRowsCount(0L)
                     .minSequenceNumber(0L)
                     .build())
         .isInstanceOf(IllegalArgumentException.class)
@@ -434,10 +535,12 @@ class TestManifestInfoStruct {
                     .existingFilesCount(0)
                     .deletedFilesCount(0)
                     .replacedFilesCount(0)
+                    .modifiedFilesCount(0)
                     .addedRowsCount(0L)
                     .existingRowsCount(5L)
                     .deletedRowsCount(0L)
                     .replacedRowsCount(0L)
+                    .modifiedRowsCount(0L)
                     .minSequenceNumber(0L)
                     .build())
         .isInstanceOf(IllegalArgumentException.class)
@@ -450,10 +553,12 @@ class TestManifestInfoStruct {
                     .existingFilesCount(0)
                     .deletedFilesCount(0)
                     .replacedFilesCount(0)
+                    .modifiedFilesCount(0)
                     .addedRowsCount(0L)
                     .existingRowsCount(0L)
                     .deletedRowsCount(3L)
                     .replacedRowsCount(0L)
+                    .modifiedRowsCount(0L)
                     .minSequenceNumber(0L)
                     .build())
         .isInstanceOf(IllegalArgumentException.class)
@@ -466,14 +571,34 @@ class TestManifestInfoStruct {
                     .existingFilesCount(0)
                     .deletedFilesCount(0)
                     .replacedFilesCount(0)
+                    .modifiedFilesCount(0)
                     .addedRowsCount(0L)
                     .existingRowsCount(0L)
                     .deletedRowsCount(0L)
                     .replacedRowsCount(7L)
+                    .modifiedRowsCount(0L)
                     .minSequenceNumber(0L)
                     .build())
         .isInstanceOf(IllegalArgumentException.class)
         .hasMessage("Invalid replaced counts: 7 rows in 0 files");
+
+    assertThatThrownBy(
+            () ->
+                ManifestInfoStruct.builder()
+                    .addedFilesCount(0)
+                    .existingFilesCount(0)
+                    .deletedFilesCount(0)
+                    .replacedFilesCount(0)
+                    .modifiedFilesCount(0)
+                    .addedRowsCount(0L)
+                    .existingRowsCount(0L)
+                    .deletedRowsCount(0L)
+                    .replacedRowsCount(0L)
+                    .modifiedRowsCount(4L)
+                    .minSequenceNumber(0L)
+                    .build())
+        .isInstanceOf(IllegalArgumentException.class)
+        .hasMessage("Invalid modified counts: 4 rows in 0 files");
   }
 
   @Test
@@ -484,10 +609,12 @@ class TestManifestInfoStruct {
             .existingFilesCount(5)
             .deletedFilesCount(5)
             .replacedFilesCount(5)
+            .modifiedFilesCount(5)
             .addedRowsCount(0L)
             .existingRowsCount(0L)
             .deletedRowsCount(0L)
             .replacedRowsCount(0L)
+            .modifiedRowsCount(0L)
             .minSequenceNumber(0L)
             .build();
 
@@ -495,10 +622,12 @@ class TestManifestInfoStruct {
     assertThat(info.existingFilesCount()).isEqualTo(5);
     assertThat(info.deletedFilesCount()).isEqualTo(5);
     assertThat(info.replacedFilesCount()).isEqualTo(5);
+    assertThat(info.modifiedFilesCount()).isEqualTo(5);
     assertThat(info.addedRowsCount()).isEqualTo(0L);
     assertThat(info.existingRowsCount()).isEqualTo(0L);
     assertThat(info.deletedRowsCount()).isEqualTo(0L);
     assertThat(info.replacedRowsCount()).isEqualTo(0L);
+    assertThat(info.modifiedRowsCount()).isEqualTo(0L);
   }
 
   @Test
@@ -509,10 +638,12 @@ class TestManifestInfoStruct {
             .existingFilesCount(20)
             .deletedFilesCount(3)
             .replacedFilesCount(2)
+            .modifiedFilesCount(1)
             .addedRowsCount(1000L)
             .existingRowsCount(2000L)
             .deletedRowsCount(300L)
             .replacedRowsCount(200L)
+            .modifiedRowsCount(100L)
             .minSequenceNumber(5L)
             .dv(ByteBuffer.wrap(MumblingTestUtil.onlyFirstBitSetBytes()))
             .build();
@@ -523,10 +654,12 @@ class TestManifestInfoStruct {
     assertThat(deserialized.existingFilesCount()).isEqualTo(20);
     assertThat(deserialized.deletedFilesCount()).isEqualTo(3);
     assertThat(deserialized.replacedFilesCount()).isEqualTo(2);
+    assertThat(deserialized.modifiedFilesCount()).isEqualTo(1);
     assertThat(deserialized.addedRowsCount()).isEqualTo(1000L);
     assertThat(deserialized.existingRowsCount()).isEqualTo(2000L);
     assertThat(deserialized.deletedRowsCount()).isEqualTo(300L);
     assertThat(deserialized.replacedRowsCount()).isEqualTo(200L);
+    assertThat(deserialized.modifiedRowsCount()).isEqualTo(100L);
     assertThat(deserialized.minSequenceNumber()).isEqualTo(5L);
     assertThat(deserialized.manifestDeletionVector().buffer())
         .isEqualTo(ByteBuffer.wrap(MumblingTestUtil.onlyFirstBitSetBytes()));
diff --git a/core/src/test/java/org/apache/iceberg/TestTrackedFileAdapters.java 
b/core/src/test/java/org/apache/iceberg/TestTrackedFileAdapters.java
index a515f0fef0..a7bfe777c1 100644
--- a/core/src/test/java/org/apache/iceberg/TestTrackedFileAdapters.java
+++ b/core/src/test/java/org/apache/iceberg/TestTrackedFileAdapters.java
@@ -115,10 +115,12 @@ class TestTrackedFileAdapters {
           .existingFilesCount(5)
           .deletedFilesCount(2)
           .replacedFilesCount(0)
+          .modifiedFilesCount(0)
           .addedRowsCount(300L)
           .existingRowsCount(500L)
           .deletedRowsCount(200L)
           .replacedRowsCount(0L)
+          .modifiedRowsCount(0L)
           .minSequenceNumber(7L)
           .dv(ByteBuffer.wrap(MumblingTestUtil.onlyFirstBitSetBytes()))
           .build();
diff --git a/core/src/test/java/org/apache/iceberg/TestV4ManifestReader.java 
b/core/src/test/java/org/apache/iceberg/TestV4ManifestReader.java
index 94e448e8b8..92104ac90c 100644
--- a/core/src/test/java/org/apache/iceberg/TestV4ManifestReader.java
+++ b/core/src/test/java/org/apache/iceberg/TestV4ManifestReader.java
@@ -75,7 +75,7 @@ class TestV4ManifestReader {
   private static final Tracking ADDED_TRACKING = 
TrackingBuilder.added(SNAPSHOT_ID).build();
 
   private static final ManifestInfo MANIFEST_INFO =
-      new ManifestInfoStruct(49, 51, 0, 0, 4_900L, 5_100L, 0L, 0L, 1L, null);
+      new ManifestInfoStruct(49, 51, 0, 0, 1, 4_900L, 5_100L, 0L, 0L, 100L, 
1L, null);
 
   private static final Schema TABLE_SCHEMA =
       new Schema(
diff --git a/core/src/test/java/org/apache/iceberg/V4TestComparators.java 
b/core/src/test/java/org/apache/iceberg/V4TestComparators.java
index 4905d6032f..9c3227afd7 100644
--- a/core/src/test/java/org/apache/iceberg/V4TestComparators.java
+++ b/core/src/test/java/org/apache/iceberg/V4TestComparators.java
@@ -114,10 +114,12 @@ class V4TestComparators {
               .thenComparingInt(ManifestInfo::existingFilesCount)
               .thenComparingInt(ManifestInfo::deletedFilesCount)
               .thenComparingInt(ManifestInfo::replacedFilesCount)
+              .thenComparingInt(ManifestInfo::modifiedFilesCount)
               .thenComparingLong(ManifestInfo::addedRowsCount)
               .thenComparingLong(ManifestInfo::existingRowsCount)
               .thenComparingLong(ManifestInfo::deletedRowsCount)
               .thenComparingLong(ManifestInfo::replacedRowsCount)
+              .thenComparingLong(ManifestInfo::modifiedRowsCount)
               .thenComparingLong(ManifestInfo::minSequenceNumber)
               .thenComparing(ManifestInfo::manifestDeletionVector, BITMAPS));
 

Reply via email to