This is an automated email from the ASF dual-hosted git repository.
danielcweeks 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 063a80a9a7 Core: Move v4 format_version from tracked file to manifest
info (#18418)
063a80a9a7 is described below
commit 063a80a9a7427794890dae740c08521627100ee4
Author: Anoop Johnson <[email protected]>
AuthorDate: Fri Oct 9 11:10:57 2026 -0700
Core: Move v4 format_version from tracked file to manifest info (#18418)
Move the v4 format_version field (0: PRE-V4, 4: V4) off every tracked file
and onto manifest_info as a required field (ID 521), so it is tracked only for
manifest entries. The tracked_file field ID 157 is removed, and the
TrackedManifestFile adapter sources formatVersion() from manifest_info.
---
.../main/java/org/apache/iceberg/ManifestInfo.java | 12 +++-
.../org/apache/iceberg/ManifestInfoStruct.java | 32 +++++++++-
.../main/java/org/apache/iceberg/TrackedFile.java | 7 ---
.../org/apache/iceberg/TrackedFileAdapters.java | 2 +-
.../java/org/apache/iceberg/TrackedFileStruct.java | 65 ++++++++-----------
.../java/org/apache/iceberg/StatsTestUtil.java | 1 -
.../org/apache/iceberg/TestManifestInfoStruct.java | 72 +++++++++++++++++++++-
.../java/org/apache/iceberg/TestTrackedFile.java | 4 +-
.../apache/iceberg/TestTrackedFileAdapters.java | 9 +--
.../org/apache/iceberg/TestTrackedFileStruct.java | 17 -----
.../org/apache/iceberg/TestV4ManifestReader.java | 10 +--
.../java/org/apache/iceberg/V4TestComparators.java | 4 +-
.../java/org/apache/iceberg/V4TestHelpers.java | 1 -
13 files changed, 143 insertions(+), 93 deletions(-)
diff --git a/core/src/main/java/org/apache/iceberg/ManifestInfo.java
b/core/src/main/java/org/apache/iceberg/ManifestInfo.java
index c41a008526..45d5062aab 100644
--- a/core/src/main/java/org/apache/iceberg/ManifestInfo.java
+++ b/core/src/main/java/org/apache/iceberg/ManifestInfo.java
@@ -61,6 +61,12 @@ interface ManifestInfo {
Types.NestedField DV =
Types.NestedField.optional(
522, "dv", Types.BinaryType.get(), "Deletion vector for manifest
entries");
+ Types.NestedField FORMAT_VERSION =
+ Types.NestedField.required(
+ 521,
+ "format_version",
+ Types.IntegerType.get(),
+ "Format version used to write the manifest");
static Types.StructType schema() {
return Types.StructType.of(
@@ -75,7 +81,8 @@ interface ManifestInfo {
REPLACED_ROWS_COUNT,
MODIFIED_ROWS_COUNT,
MIN_SEQUENCE_NUMBER,
- DV);
+ DV,
+ FORMAT_VERSION);
}
/** Returns the number of files added by this manifest. */
@@ -114,6 +121,9 @@ interface ManifestInfo {
/** Returns the deletion vector for manifest entries, or null if not
present. */
ManifestBitmap manifestDeletionVector();
+ /** Returns the format version used to write the manifest. */
+ int formatVersion();
+
/** Copies this manifest info. */
ManifestInfo copy();
}
diff --git a/core/src/main/java/org/apache/iceberg/ManifestInfoStruct.java
b/core/src/main/java/org/apache/iceberg/ManifestInfoStruct.java
index 26a2fcef76..bc5765a855 100644
--- a/core/src/main/java/org/apache/iceberg/ManifestInfoStruct.java
+++ b/core/src/main/java/org/apache/iceberg/ManifestInfoStruct.java
@@ -43,7 +43,8 @@ class ManifestInfoStruct extends SupportsIndexProjection
implements ManifestInfo
ManifestInfo.REPLACED_ROWS_COUNT,
ManifestInfo.MODIFIED_ROWS_COUNT,
ManifestInfo.MIN_SEQUENCE_NUMBER,
- ManifestInfo.DV);
+ ManifestInfo.DV,
+ ManifestInfo.FORMAT_VERSION);
private int addedFilesCount = -1;
private int existingFilesCount = -1;
@@ -57,6 +58,7 @@ class ManifestInfoStruct extends SupportsIndexProjection
implements ManifestInfo
private long modifiedRowsCount = -1L;
private long minSequenceNumber = -1L;
private byte[] dv = null;
+ private int formatVersion = -1;
private transient ManifestBitmap mdv = null;
@@ -78,6 +80,7 @@ class ManifestInfoStruct extends SupportsIndexProjection
implements ManifestInfo
this.modifiedRowsCount = toCopy.modifiedRowsCount;
this.minSequenceNumber = toCopy.minSequenceNumber;
this.dv = toCopy.dv != null ? Arrays.copyOf(toCopy.dv, toCopy.dv.length) :
null;
+ this.formatVersion = toCopy.formatVersion;
}
ManifestInfoStruct(
@@ -92,7 +95,8 @@ class ManifestInfoStruct extends SupportsIndexProjection
implements ManifestInfo
long replacedRowsCount,
long modifiedRowsCount,
long minSequenceNumber,
- byte[] dv) {
+ byte[] dv,
+ int formatVersion) {
super(BASE_TYPE.fields().size());
this.addedFilesCount = addedFilesCount;
this.existingFilesCount = existingFilesCount;
@@ -106,6 +110,7 @@ class ManifestInfoStruct extends SupportsIndexProjection
implements ManifestInfo
this.modifiedRowsCount = modifiedRowsCount;
this.minSequenceNumber = minSequenceNumber;
this.dv = dv;
+ this.formatVersion = formatVersion;
}
@Override
@@ -172,6 +177,11 @@ class ManifestInfoStruct extends SupportsIndexProjection
implements ManifestInfo
return mdv;
}
+ @Override
+ public int formatVersion() {
+ return formatVersion;
+ }
+
@Override
public ManifestInfoStruct copy() {
return new ManifestInfoStruct(this);
@@ -208,6 +218,8 @@ class ManifestInfoStruct extends SupportsIndexProjection
implements ManifestInfo
return minSequenceNumber;
case 11:
return dv != null ? ByteBuffer.wrap(dv) : null;
+ case 12:
+ return formatVersion;
default:
throw new UnsupportedOperationException("Unknown field ordinal: " +
pos);
}
@@ -253,6 +265,9 @@ class ManifestInfoStruct extends SupportsIndexProjection
implements ManifestInfo
this.dv = ByteBuffers.toByteArray((ByteBuffer) value);
this.mdv = null;
break;
+ case 12:
+ this.formatVersion = (Integer) value;
+ break;
default:
// ignore the object, it must be from a newer version of the format
}
@@ -277,6 +292,7 @@ class ManifestInfoStruct extends SupportsIndexProjection
implements ManifestInfo
.add("modified_rows_count", modifiedRowsCount)
.add("min_sequence_number", minSequenceNumber)
.add("dv", dv == null ? "null" : "(binary)")
+ .add("format_version", formatVersion)
.toString();
}
@@ -293,6 +309,7 @@ class ManifestInfoStruct extends SupportsIndexProjection
implements ManifestInfo
private Long modifiedRowsCount = null;
private Long minSequenceNumber = null;
private byte[] dv = null;
+ private Integer formatVersion = null;
Builder addedFilesCount(int count) {
Preconditions.checkArgument(
@@ -376,6 +393,13 @@ class ManifestInfoStruct extends SupportsIndexProjection
implements ManifestInfo
return this;
}
+ Builder formatVersion(int version) {
+ Preconditions.checkArgument(
+ version == 0 || version == 4, "Invalid format version: %s (must be 0
or 4)", version);
+ this.formatVersion = version;
+ return this;
+ }
+
ManifestInfoStruct build() {
Preconditions.checkArgument(
addedFilesCount != null, "Missing required value: added files
count");
@@ -399,6 +423,7 @@ class ManifestInfoStruct extends SupportsIndexProjection
implements ManifestInfo
modifiedRowsCount != null, "Missing required value: modified rows
count");
Preconditions.checkArgument(
minSequenceNumber != null, "Missing required value: min sequence
number");
+ Preconditions.checkArgument(formatVersion != null, "Missing required
value: format version");
Preconditions.checkArgument(
addedRowsCount == 0 || addedFilesCount > 0,
"Invalid added counts: %s rows in %s files",
@@ -436,7 +461,8 @@ class ManifestInfoStruct extends SupportsIndexProjection
implements ManifestInfo
replacedRowsCount,
modifiedRowsCount,
minSequenceNumber,
- dv);
+ dv,
+ formatVersion);
}
}
}
diff --git a/core/src/main/java/org/apache/iceberg/TrackedFile.java
b/core/src/main/java/org/apache/iceberg/TrackedFile.java
index e2db837c48..94c55e471a 100644
--- a/core/src/main/java/org/apache/iceberg/TrackedFile.java
+++ b/core/src/main/java/org/apache/iceberg/TrackedFile.java
@@ -37,9 +37,6 @@ interface TrackedFile {
"content_type",
Types.IntegerType.get(),
"Type of content: 0=DATA, 2=EQUALITY_DELETES, 3=DATA_MANIFEST,
4=DELETE_MANIFEST");
- Types.NestedField FORMAT_VERSION =
- Types.NestedField.required(
- 157, "format_version", Types.IntegerType.get(), "Format version of
this file");
Types.NestedField LOCATION =
Types.NestedField.required(100, "location", Types.StringType.get(),
"Location of the file");
Types.NestedField FILE_FORMAT =
@@ -102,7 +99,6 @@ interface TrackedFile {
return ImmutableList.of(
TRACKING,
CONTENT_TYPE,
- FORMAT_VERSION,
LOCATION,
FILE_FORMAT,
RECORD_COUNT,
@@ -151,9 +147,6 @@ interface TrackedFile {
/** Returns the type of content stored by this entry. */
FileContent contentType();
- /** Returns the format version of this file. */
- int formatVersion();
-
/** Returns the location of the file. */
String location();
diff --git a/core/src/main/java/org/apache/iceberg/TrackedFileAdapters.java
b/core/src/main/java/org/apache/iceberg/TrackedFileAdapters.java
index 5acb03b908..2b351fc4bd 100644
--- a/core/src/main/java/org/apache/iceberg/TrackedFileAdapters.java
+++ b/core/src/main/java/org/apache/iceberg/TrackedFileAdapters.java
@@ -520,7 +520,7 @@ class TrackedFileAdapters {
@Override
public int formatVersion() {
- return file.formatVersion();
+ return file.manifestInfo().formatVersion();
}
@Override
diff --git a/core/src/main/java/org/apache/iceberg/TrackedFileStruct.java
b/core/src/main/java/org/apache/iceberg/TrackedFileStruct.java
index fffd822a56..fc400a4bfb 100644
--- a/core/src/main/java/org/apache/iceberg/TrackedFileStruct.java
+++ b/core/src/main/java/org/apache/iceberg/TrackedFileStruct.java
@@ -40,7 +40,6 @@ class TrackedFileStruct extends SupportsIndexProjection
implements TrackedFile,
Types.StructType.of(
TrackedFile.TRACKING,
TrackedFile.CONTENT_TYPE,
- TrackedFile.FORMAT_VERSION,
TrackedFile.LOCATION,
TrackedFile.FILE_FORMAT,
TrackedFile.RECORD_COUNT,
@@ -64,7 +63,6 @@ class TrackedFileStruct extends SupportsIndexProjection
implements TrackedFile,
TrackedFile.EQUALITY_IDS);
private FileContent contentType = null;
- private int formatVersion = -1;
private String location = null;
private FileFormat fileFormat = null;
private Tracking tracking = null;
@@ -97,7 +95,6 @@ class TrackedFileStruct extends SupportsIndexProjection
implements TrackedFile,
TrackedFileStruct(
Tracking tracking,
FileContent contentType,
- int formatVersion,
String location,
FileFormat fileFormat,
long recordCount,
@@ -114,7 +111,6 @@ class TrackedFileStruct extends SupportsIndexProjection
implements TrackedFile,
super(BASE_TYPE.fields().size());
this.tracking = tracking;
this.contentType = contentType;
- this.formatVersion = formatVersion;
this.location = location;
this.fileFormat = fileFormat;
this.recordCount = recordCount;
@@ -134,7 +130,6 @@ class TrackedFileStruct extends SupportsIndexProjection
implements TrackedFile,
private TrackedFileStruct(TrackedFileStruct toCopy, Set<Integer> statsIds) {
super(toCopy);
this.contentType = toCopy.contentType;
- this.formatVersion = toCopy.formatVersion;
this.location = toCopy.location;
this.fileFormat = toCopy.fileFormat;
this.recordCount = toCopy.recordCount;
@@ -177,11 +172,6 @@ class TrackedFileStruct extends SupportsIndexProjection
implements TrackedFile,
return contentType;
}
- @Override
- public int formatVersion() {
- return formatVersion;
- }
-
@Override
public String location() {
return location;
@@ -286,20 +276,19 @@ class TrackedFileStruct extends SupportsIndexProjection
implements TrackedFile,
return switch (pos) {
case 0 -> tracking;
case 1 -> contentType != null ? contentType.id() : null;
- case 2 -> formatVersion;
- case 3 -> location;
- case 4 -> fileFormat != null ? fileFormat.toString() : null;
- case 5 -> recordCount;
- case 6 -> fileSizeInBytes;
- case 7 -> specId;
- case 8 -> partition;
- case 9 -> contentStats;
- case 10 -> sortOrderId;
- case 11 -> deletionVector;
- case 12 -> manifestInfo;
- case 13 -> keyMetadata();
- case 14 -> splitOffsets();
- case 15 -> equalityIds();
+ case 2 -> location;
+ case 3 -> fileFormat != null ? fileFormat.toString() : null;
+ case 4 -> recordCount;
+ case 5 -> fileSizeInBytes;
+ case 6 -> specId;
+ case 7 -> partition;
+ case 8 -> contentStats;
+ case 9 -> sortOrderId;
+ case 10 -> deletionVector;
+ case 11 -> manifestInfo;
+ case 12 -> keyMetadata();
+ case 13 -> splitOffsets();
+ case 14 -> equalityIds();
default -> throw new UnsupportedOperationException("Unknown field
ordinal: " + pos);
};
}
@@ -309,22 +298,21 @@ class TrackedFileStruct extends SupportsIndexProjection
implements TrackedFile,
switch (pos) {
case 0 -> this.tracking = (Tracking) value;
case 1 -> this.contentType = FileContent.fromId((Integer) value);
- case 2 -> this.formatVersion = (int) value;
- case 3 ->
+ case 2 ->
// always coerce to String for Serializable
this.location = value.toString();
- case 4 -> this.fileFormat = FileFormat.fromString(value.toString());
- case 5 -> this.recordCount = (long) value;
- case 6 -> this.fileSizeInBytes = (long) value;
- case 7 -> this.specId = (Integer) value;
- case 8 -> this.partition = (StructLike) value;
- case 9 -> this.contentStats = (ContentStats) value;
- case 10 -> this.sortOrderId = (Integer) value;
- case 11 -> this.deletionVector = (DeletionVector) value;
- case 12 -> this.manifestInfo = (ManifestInfo) value;
- case 13 -> this.keyMetadata = ByteBuffers.toByteArray((ByteBuffer)
value);
- case 14 -> this.splitOffsets = ArrayUtil.toLongArray((List<Long>) value);
- case 15 -> this.equalityIds = ArrayUtil.toIntArray((List<Integer>)
value);
+ case 3 -> this.fileFormat = FileFormat.fromString(value.toString());
+ case 4 -> this.recordCount = (long) value;
+ case 5 -> this.fileSizeInBytes = (long) value;
+ case 6 -> this.specId = (Integer) value;
+ case 7 -> this.partition = (StructLike) value;
+ case 8 -> this.contentStats = (ContentStats) value;
+ case 9 -> this.sortOrderId = (Integer) value;
+ case 10 -> this.deletionVector = (DeletionVector) value;
+ case 11 -> this.manifestInfo = (ManifestInfo) value;
+ case 12 -> this.keyMetadata = ByteBuffers.toByteArray((ByteBuffer)
value);
+ case 13 -> this.splitOffsets = ArrayUtil.toLongArray((List<Long>) value);
+ case 14 -> this.equalityIds = ArrayUtil.toIntArray((List<Integer>)
value);
default -> {
// ignore the object, it must be from a newer version of the format
}
@@ -335,7 +323,6 @@ class TrackedFileStruct extends SupportsIndexProjection
implements TrackedFile,
public String toString() {
return MoreObjects.toStringHelper(this)
.add("content", contentType != null ? contentType.lowerCaseName() :
null)
- .add("format_version", formatVersion)
.add("location", location)
.add("file_format", fileFormat)
.add("record_count", recordCount)
diff --git a/core/src/test/java/org/apache/iceberg/StatsTestUtil.java
b/core/src/test/java/org/apache/iceberg/StatsTestUtil.java
index 93309ceb9b..a2bbf7bf53 100644
--- a/core/src/test/java/org/apache/iceberg/StatsTestUtil.java
+++ b/core/src/test/java/org/apache/iceberg/StatsTestUtil.java
@@ -29,7 +29,6 @@ class StatsTestUtil {
return new TrackedFileStruct(
null,
FileContent.DATA,
- 4,
location,
FileFormat.fromFileName(location),
recordCount,
diff --git a/core/src/test/java/org/apache/iceberg/TestManifestInfoStruct.java
b/core/src/test/java/org/apache/iceberg/TestManifestInfoStruct.java
index 1892f2d890..b1363249bb 100644
--- a/core/src/test/java/org/apache/iceberg/TestManifestInfoStruct.java
+++ b/core/src/test/java/org/apache/iceberg/TestManifestInfoStruct.java
@@ -26,6 +26,8 @@ import java.nio.ByteBuffer;
import org.apache.iceberg.mumbling.MumblingTestUtil;
import org.apache.iceberg.types.Types;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
class TestManifestInfoStruct {
@@ -44,7 +46,8 @@ class TestManifestInfoStruct {
200L,
100L,
5L,
- MumblingTestUtil.onlyFirstBitSetBytes());
+ MumblingTestUtil.onlyFirstBitSetBytes(),
+ 4);
assertThat(info.addedFilesCount()).isEqualTo(10);
assertThat(info.existingFilesCount()).isEqualTo(20);
@@ -60,6 +63,7 @@ class TestManifestInfoStruct {
assertThat(info.manifestDeletionVector().buffer())
.isEqualTo(ByteBuffer.wrap(MumblingTestUtil.onlyFirstBitSetBytes()));
assertThat(info.manifestDeletionVector().cardinality()).isEqualTo(1);
+ assertThat(info.formatVersion()).isEqualTo(4);
}
@Test
@@ -78,6 +82,7 @@ class TestManifestInfoStruct {
.modifiedRowsCount(100L)
.minSequenceNumber(5L)
.dv(ByteBuffer.wrap(MumblingTestUtil.onlyFirstBitSetBytes()))
+ .formatVersion(4)
.build();
ManifestInfoStruct copy = info.copy();
@@ -93,6 +98,7 @@ class TestManifestInfoStruct {
assertThat(copy.replacedRowsCount()).isEqualTo(200L);
assertThat(copy.modifiedRowsCount()).isEqualTo(100L);
assertThat(copy.minSequenceNumber()).isEqualTo(5L);
+ assertThat(copy.formatVersion()).isEqualTo(4);
// verify deep copy of dv byte array
assertThat(copy.manifestDeletionVector().buffer().array())
@@ -114,6 +120,7 @@ class TestManifestInfoStruct {
.replacedRowsCount(0L)
.modifiedRowsCount(0L)
.minSequenceNumber(0L)
+ .formatVersion(4)
.build();
assertThat(info.manifestDeletionVector()).isNull();
@@ -155,6 +162,7 @@ class TestManifestInfoStruct {
.modifiedRowsCount(100L)
.minSequenceNumber(5L)
.dv(ByteBuffer.wrap(MumblingTestUtil.onlyFirstBitSetBytes()))
+ .formatVersion(4)
.build();
ManifestBitmap mdv = info.manifestDeletionVector();
@@ -173,6 +181,7 @@ class TestManifestInfoStruct {
assertThat(info.replacedRowsCount()).isEqualTo(200L);
assertThat(info.modifiedRowsCount()).isEqualTo(100L);
assertThat(info.minSequenceNumber()).isEqualTo(5L);
+ assertThat(info.formatVersion()).isEqualTo(4);
assertThat(info.manifestDeletionVector()).isSameAs(mdv);
}
@@ -192,6 +201,7 @@ class TestManifestInfoStruct {
.modifiedRowsCount(100L)
.minSequenceNumber(5L)
.dv(ByteBuffer.wrap(MumblingTestUtil.onlyFirstBitSetBytes()))
+ .formatVersion(4)
.build();
ManifestInfoStruct deserialized = TestHelpers.roundTripSerialize(info);
@@ -207,6 +217,7 @@ class TestManifestInfoStruct {
assertThat(deserialized.replacedRowsCount()).isEqualTo(200L);
assertThat(deserialized.modifiedRowsCount()).isEqualTo(100L);
assertThat(deserialized.minSequenceNumber()).isEqualTo(5L);
+ assertThat(deserialized.formatVersion()).isEqualTo(4);
assertThat(deserialized.manifestDeletionVector().buffer())
.isEqualTo(ByteBuffer.wrap(MumblingTestUtil.onlyFirstBitSetBytes()));
}
@@ -524,6 +535,7 @@ class TestManifestInfoStruct {
.replacedRowsCount(0L)
.modifiedRowsCount(0L)
.minSequenceNumber(0L)
+ .formatVersion(4)
.build())
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Invalid added counts: 10 rows in 0 files");
@@ -542,6 +554,7 @@ class TestManifestInfoStruct {
.replacedRowsCount(0L)
.modifiedRowsCount(0L)
.minSequenceNumber(0L)
+ .formatVersion(4)
.build())
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Invalid existing counts: 5 rows in 0 files");
@@ -560,6 +573,7 @@ class TestManifestInfoStruct {
.replacedRowsCount(0L)
.modifiedRowsCount(0L)
.minSequenceNumber(0L)
+ .formatVersion(4)
.build())
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Invalid deleted counts: 3 rows in 0 files");
@@ -578,6 +592,7 @@ class TestManifestInfoStruct {
.replacedRowsCount(7L)
.modifiedRowsCount(0L)
.minSequenceNumber(0L)
+ .formatVersion(4)
.build())
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Invalid replaced counts: 7 rows in 0 files");
@@ -596,6 +611,7 @@ class TestManifestInfoStruct {
.replacedRowsCount(0L)
.modifiedRowsCount(4L)
.minSequenceNumber(0L)
+ .formatVersion(4)
.build())
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Invalid modified counts: 4 rows in 0 files");
@@ -616,6 +632,7 @@ class TestManifestInfoStruct {
.replacedRowsCount(0L)
.modifiedRowsCount(0L)
.minSequenceNumber(0L)
+ .formatVersion(4)
.build();
assertThat(info.addedFilesCount()).isEqualTo(5);
@@ -646,6 +663,7 @@ class TestManifestInfoStruct {
.modifiedRowsCount(100L)
.minSequenceNumber(5L)
.dv(ByteBuffer.wrap(MumblingTestUtil.onlyFirstBitSetBytes()))
+ .formatVersion(4)
.build();
ManifestInfoStruct deserialized =
TestHelpers.KryoHelpers.roundTripSerialize(info);
@@ -661,7 +679,59 @@ class TestManifestInfoStruct {
assertThat(deserialized.replacedRowsCount()).isEqualTo(200L);
assertThat(deserialized.modifiedRowsCount()).isEqualTo(100L);
assertThat(deserialized.minSequenceNumber()).isEqualTo(5L);
+ assertThat(deserialized.formatVersion()).isEqualTo(4);
assertThat(deserialized.manifestDeletionVector().buffer())
.isEqualTo(ByteBuffer.wrap(MumblingTestUtil.onlyFirstBitSetBytes()));
}
+
+ @Test
+ void builderMissingFormatVersion() {
+ assertThatThrownBy(
+ () ->
+ ManifestInfoStruct.builder()
+ .addedFilesCount(0)
+ .existingFilesCount(0)
+ .deletedFilesCount(0)
+ .replacedFilesCount(0)
+ .modifiedFilesCount(0)
+ .addedRowsCount(0L)
+ .existingRowsCount(0L)
+ .deletedRowsCount(0L)
+ .replacedRowsCount(0L)
+ .modifiedRowsCount(0L)
+ .minSequenceNumber(0L)
+ .build())
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Missing required value: format version");
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {-1, 1, 2, 3, 5})
+ void builderRejectsUndefinedFormatVersion(int version) {
+ assertThatThrownBy(() ->
ManifestInfoStruct.builder().formatVersion(version))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Invalid format version: " + version + " (must be 0 or
4)");
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {0, 4})
+ void builderAcceptsValidFormatVersion(int version) {
+ ManifestInfoStruct info =
+ ManifestInfoStruct.builder()
+ .addedFilesCount(0)
+ .existingFilesCount(0)
+ .deletedFilesCount(0)
+ .replacedFilesCount(0)
+ .modifiedFilesCount(0)
+ .addedRowsCount(0L)
+ .existingRowsCount(0L)
+ .deletedRowsCount(0L)
+ .replacedRowsCount(0L)
+ .modifiedRowsCount(0L)
+ .minSequenceNumber(0L)
+ .formatVersion(version)
+ .build();
+
+ assertThat(info.formatVersion()).isEqualTo(version);
+ }
}
diff --git a/core/src/test/java/org/apache/iceberg/TestTrackedFile.java
b/core/src/test/java/org/apache/iceberg/TestTrackedFile.java
index 9a32243cc1..e0a1ef19cc 100644
--- a/core/src/test/java/org/apache/iceberg/TestTrackedFile.java
+++ b/core/src/test/java/org/apache/iceberg/TestTrackedFile.java
@@ -48,7 +48,6 @@ public class TestTrackedFile {
.containsExactly(
"tracking",
"content_type",
- "format_version",
"location",
"file_format",
"record_count",
@@ -71,8 +70,7 @@ public class TestTrackedFile {
assertThat(fields)
.extracting(Types.NestedField::fieldId)
- .containsExactly(
- 147, 134, 157, 100, 101, 103, 104, 141, 102, 146, 140, 148, 150,
131, 132, 135);
+ .containsExactly(147, 134, 100, 101, 103, 104, 141, 102, 146, 140,
148, 150, 131, 132, 135);
}
@Test
diff --git a/core/src/test/java/org/apache/iceberg/TestTrackedFileAdapters.java
b/core/src/test/java/org/apache/iceberg/TestTrackedFileAdapters.java
index d3af965ec1..31d6758b80 100644
--- a/core/src/test/java/org/apache/iceberg/TestTrackedFileAdapters.java
+++ b/core/src/test/java/org/apache/iceberg/TestTrackedFileAdapters.java
@@ -123,6 +123,7 @@ class TestTrackedFileAdapters {
.modifiedRowsCount(0L)
.minSequenceNumber(7L)
.dv(ByteBuffer.wrap(MumblingTestUtil.onlyFirstBitSetBytes()))
+ .formatVersion(FORMAT_VERSION_V4)
.build();
@Test
@@ -145,7 +146,6 @@ class TestTrackedFileAdapters {
new TrackedFileStruct(
tracking,
FileContent.DATA,
- FORMAT_VERSION_V4,
DATA_FILE_LOCATION,
FileFormat.PARQUET,
100L,
@@ -225,7 +225,6 @@ class TestTrackedFileAdapters {
new TrackedFileStruct(
tracking,
FileContent.EQUALITY_DELETES,
- FORMAT_VERSION_V4,
"s3://bucket/eq-delete.avro",
FileFormat.AVRO,
50L,
@@ -314,7 +313,6 @@ class TestTrackedFileAdapters {
new TrackedFileStruct(
tracking,
FileContent.DATA,
- FORMAT_VERSION_V4,
DATA_FILE_LOCATION,
FileFormat.PARQUET,
100L,
@@ -446,7 +444,6 @@ class TestTrackedFileAdapters {
return new TrackedFileStruct(
tracking,
FileContent.DATA,
- FORMAT_VERSION_V4,
DATA_FILE_LOCATION,
FileFormat.PARQUET,
100L, // recordCount
@@ -490,7 +487,6 @@ class TestTrackedFileAdapters {
new TrackedFileStruct(
MANIFEST_TRACKING,
contentType,
- FORMAT_VERSION_V4,
MANIFEST_LOCATION,
FileFormat.PARQUET,
10L, // recordCount
@@ -597,7 +593,6 @@ class TestTrackedFileAdapters {
new TrackedFileStruct(
null,
FileContent.DATA,
- 0,
null,
null,
0L,
@@ -651,7 +646,6 @@ class TestTrackedFileAdapters {
new TrackedFileStruct(
null,
FileContent.DATA,
- 0,
null,
null,
0L,
@@ -695,7 +689,6 @@ class TestTrackedFileAdapters {
return new TrackedFileStruct(
null,
contentType,
- FORMAT_VERSION_V4,
DATA_FILE_LOCATION,
FileFormat.PARQUET,
1L,
diff --git a/core/src/test/java/org/apache/iceberg/TestTrackedFileStruct.java
b/core/src/test/java/org/apache/iceberg/TestTrackedFileStruct.java
index f5edda5567..d5e31215b8 100644
--- a/core/src/test/java/org/apache/iceberg/TestTrackedFileStruct.java
+++ b/core/src/test/java/org/apache/iceberg/TestTrackedFileStruct.java
@@ -40,8 +40,6 @@ import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Mockito;
class TestTrackedFileStruct {
- private static final int FORMAT_VERSION_V4 = 4;
-
private static final List<Types.NestedField> DEFAULT_FIELDS =
TrackedFile.schema(Types.StructType.of(),
Types.StructType.of()).asStruct().fields();
@@ -72,7 +70,6 @@ class TestTrackedFileStruct {
new TrackedFileStruct(
TRACKING,
FileContent.DATA,
- FORMAT_VERSION_V4,
"s3://bucket/data/00000-0-file.parquet",
FileFormat.PARQUET,
50L,
@@ -89,7 +86,6 @@ class TestTrackedFileStruct {
assertThat(file.tracking()).isSameAs(TRACKING);
assertThat(file.contentType()).isEqualTo(FileContent.DATA);
- assertThat(file.formatVersion()).isEqualTo(FORMAT_VERSION_V4);
assertThat(file.location()).isEqualTo("s3://bucket/data/00000-0-file.parquet");
assertThat(file.fileFormat()).isEqualTo(FileFormat.PARQUET);
assertThat(file.partition()).isSameAs(PARTITION);
@@ -110,7 +106,6 @@ class TestTrackedFileStruct {
TrackedFileStruct file = new TrackedFileStruct();
file.set(pos("tracking"), TRACKING);
file.set(pos("content_type"), FileContent.DATA.id());
- file.set(pos("format_version"), FORMAT_VERSION_V4);
file.set(pos("location"), "s3://bucket/data/00000-0-file.parquet");
file.set(pos("file_format"), "parquet");
file.set(pos("record_count"), 50L);
@@ -127,7 +122,6 @@ class TestTrackedFileStruct {
assertThat(file.tracking()).isSameAs(TRACKING);
assertThat(file.contentType()).isEqualTo(FileContent.DATA);
- assertThat(file.formatVersion()).isEqualTo(FORMAT_VERSION_V4);
assertThat(file.location()).isEqualTo("s3://bucket/data/00000-0-file.parquet");
assertThat(file.fileFormat()).isEqualTo(FileFormat.PARQUET);
assertThat(file.recordCount()).isEqualTo(50L);
@@ -149,7 +143,6 @@ class TestTrackedFileStruct {
new TrackedFileStruct(
TRACKING,
FileContent.DATA,
- FORMAT_VERSION_V4,
"s3://bucket/data/00000-0-file.parquet",
FileFormat.PARQUET,
50L,
@@ -166,7 +159,6 @@ class TestTrackedFileStruct {
assertThat(file.get(pos("tracking"), Tracking.class)).isSameAs(TRACKING);
assertThat(file.get(pos("content_type"),
Integer.class)).isEqualTo(FileContent.DATA.id());
- assertThat(file.get(pos("format_version"),
Integer.class)).isEqualTo(FORMAT_VERSION_V4);
assertThat(file.get(pos("location"), String.class))
.isEqualTo("s3://bucket/data/00000-0-file.parquet");
assertThat(file.get(pos("file_format"),
String.class)).isEqualTo(FileFormat.PARQUET.toString());
@@ -190,7 +182,6 @@ class TestTrackedFileStruct {
new TrackedFileStruct(
TRACKING,
FileContent.DATA,
- FORMAT_VERSION_V4,
"s3://bucket/data/00000-0-file.parquet",
FileFormat.PARQUET,
50L,
@@ -210,7 +201,6 @@ class TestTrackedFileStruct {
assertThat(copy).isInstanceOf(TrackedFileStruct.class);
assertThat(copy.tracking()).isSameAs(TRACKING_COPY);
assertThat(copy.contentType()).isEqualTo(FileContent.DATA);
- assertThat(copy.formatVersion()).isEqualTo(FORMAT_VERSION_V4);
assertThat(copy.location()).isEqualTo("s3://bucket/data/00000-0-file.parquet");
assertThat(copy.fileFormat()).isEqualTo(FileFormat.PARQUET);
assertThat(copy.recordCount()).isEqualTo(50L);
@@ -239,7 +229,6 @@ class TestTrackedFileStruct {
new TrackedFileStruct(
TRACKING,
FileContent.DATA,
- FORMAT_VERSION_V4,
"s3://bucket/data/00000-0-file.parquet",
FileFormat.PARQUET,
50L,
@@ -259,7 +248,6 @@ class TestTrackedFileStruct {
assertThat(copy).isInstanceOf(TrackedFileStruct.class);
assertThat(copy.tracking()).isSameAs(TRACKING_COPY);
assertThat(copy.contentType()).isEqualTo(FileContent.DATA);
- assertThat(copy.formatVersion()).isEqualTo(FORMAT_VERSION_V4);
assertThat(copy.location()).isEqualTo("s3://bucket/data/00000-0-file.parquet");
assertThat(copy.fileFormat()).isEqualTo(FileFormat.PARQUET);
assertThat(copy.recordCount()).isEqualTo(50L);
@@ -286,7 +274,6 @@ class TestTrackedFileStruct {
new TrackedFileStruct(
TRACKING,
FileContent.DATA,
- FORMAT_VERSION_V4,
"s3://bucket/data/00000-0-file.parquet",
FileFormat.PARQUET,
50L,
@@ -309,7 +296,6 @@ class TestTrackedFileStruct {
assertThat(copy).isInstanceOf(TrackedFileStruct.class);
assertThat(copy.tracking()).isSameAs(TRACKING_COPY);
assertThat(copy.contentType()).isEqualTo(FileContent.DATA);
- assertThat(copy.formatVersion()).isEqualTo(FORMAT_VERSION_V4);
assertThat(copy.location()).isEqualTo("s3://bucket/data/00000-0-file.parquet");
assertThat(copy.fileFormat()).isEqualTo(FileFormat.PARQUET);
assertThat(copy.recordCount()).isEqualTo(50L);
@@ -436,7 +422,6 @@ class TestTrackedFileStruct {
new TrackedFileStruct(
null, // TrackingStruct has its own serialization tests
FileContent.DATA,
- FORMAT_VERSION_V4,
"s3://bucket/data/file.parquet",
FileFormat.PARQUET,
100L,
@@ -455,7 +440,6 @@ class TestTrackedFileStruct {
assertThat(deserialized.tracking()).isNull();
assertThat(deserialized.contentType()).isEqualTo(FileContent.DATA);
- assertThat(deserialized.formatVersion()).isEqualTo(FORMAT_VERSION_V4);
assertThat(deserialized.location()).isEqualTo("s3://bucket/data/file.parquet");
assertThat(deserialized.fileFormat()).isEqualTo(FileFormat.PARQUET);
assertThat(deserialized.partition())
@@ -513,7 +497,6 @@ class TestTrackedFileStruct {
return new TrackedFileStruct(
null, // tracking
FileContent.DATA,
- FORMAT_VERSION_V4,
"s3://bucket/file.parquet",
FileFormat.PARQUET,
100L, // recordCount
diff --git a/core/src/test/java/org/apache/iceberg/TestV4ManifestReader.java
b/core/src/test/java/org/apache/iceberg/TestV4ManifestReader.java
index f2c17bb552..6db0059742 100644
--- a/core/src/test/java/org/apache/iceberg/TestV4ManifestReader.java
+++ b/core/src/test/java/org/apache/iceberg/TestV4ManifestReader.java
@@ -82,7 +82,7 @@ class TestV4ManifestReader {
private static final DeletionVector DV =
deletionVector("s3://bucket/dv.puffin");
private static final ManifestInfo MANIFEST_INFO =
- new ManifestInfoStruct(49, 51, 0, 0, 1, 4_900L, 5_100L, 0L, 0L, 100L,
1L, null);
+ new ManifestInfoStruct(49, 51, 0, 0, 1, 4_900L, 5_100L, 0L, 0L, 100L,
1L, null, 4);
private static final Schema TABLE_SCHEMA =
new Schema(
@@ -168,7 +168,6 @@ class TestV4ManifestReader {
new TrackedFileStruct(
ADDED_TRACKING,
FileContent.DATA,
- FORMAT_VERSION_V4,
"s3://bucket/data/file.parquet",
FileFormat.PARQUET,
RECORD_COUNT,
@@ -200,7 +199,6 @@ class TestV4ManifestReader {
new TrackedFileStruct(
ADDED_TRACKING,
FileContent.DATA,
- FORMAT_VERSION_V4,
"s3://bucket/data/file.parquet",
FileFormat.PARQUET,
RECORD_COUNT,
@@ -234,7 +232,6 @@ class TestV4ManifestReader {
new TrackedFileStruct(
ADDED_TRACKING,
FileContent.DATA,
- FORMAT_VERSION_V4,
"s3://bucket/data/file.parquet",
FileFormat.PARQUET,
RECORD_COUNT,
@@ -270,7 +267,6 @@ class TestV4ManifestReader {
new TrackedFileStruct(
ADDED_TRACKING,
FileContent.EQUALITY_DELETES,
- FORMAT_VERSION_V4,
"s3://bucket/eq-delete.parquet",
FileFormat.PARQUET,
RECORD_COUNT,
@@ -302,7 +298,6 @@ class TestV4ManifestReader {
new TrackedFileStruct(
ADDED_TRACKING,
FileContent.DATA_MANIFEST,
- FORMAT_VERSION_V4,
"s3://bucket/leaf-manifest.parquet",
FileFormat.PARQUET,
RECORD_COUNT,
@@ -1163,7 +1158,6 @@ class TestV4ManifestReader {
new TrackedFileStruct(
ADDED_TRACKING,
FileContent.DATA,
- FORMAT_VERSION_V4,
"s3://bucket/table/empty-file.parquet",
FileFormat.PARQUET,
0, // file contains no records
@@ -1200,7 +1194,6 @@ class TestV4ManifestReader {
new TrackedFileStruct(
ADDED_TRACKING,
FileContent.DATA,
- FORMAT_VERSION_V4,
"s3://bucket/table/very-old.avro",
FileFormat.AVRO,
-1, // mimic invalid record count in old Avro metadata
@@ -1385,7 +1378,6 @@ class TestV4ManifestReader {
new TrackedFileStruct(
ADDED_TRACKING,
FileContent.DATA,
- FORMAT_VERSION_V4,
"s3://bucket/table/bucket=7/data-file.parquet",
FileFormat.AVRO,
10,
diff --git a/core/src/test/java/org/apache/iceberg/V4TestComparators.java
b/core/src/test/java/org/apache/iceberg/V4TestComparators.java
index 08ea44ed35..088604b5a5 100644
--- a/core/src/test/java/org/apache/iceberg/V4TestComparators.java
+++ b/core/src/test/java/org/apache/iceberg/V4TestComparators.java
@@ -55,7 +55,6 @@ class V4TestComparators {
return Comparator.nullsFirst(
Comparator.comparing(TrackedFile::tracking, trackingComparator)
.thenComparing(TrackedFile::contentType, natural())
- .thenComparingInt(TrackedFile::formatVersion)
.thenComparing(TrackedFile::location, natural())
.thenComparing(TrackedFile::fileFormat, natural())
.thenComparingLong(TrackedFile::recordCount)
@@ -121,7 +120,8 @@ class V4TestComparators {
.thenComparingLong(ManifestInfo::replacedRowsCount)
.thenComparingLong(ManifestInfo::modifiedRowsCount)
.thenComparingLong(ManifestInfo::minSequenceNumber)
- .thenComparing(ManifestInfo::manifestDeletionVector, BITMAPS));
+ .thenComparing(ManifestInfo::manifestDeletionVector, BITMAPS)
+ .thenComparingInt(ManifestInfo::formatVersion));
private static final Comparator<ContentStats> CONTENT_STATS =
Comparator.nullsFirst(new ContentStatsComparator());
diff --git a/core/src/test/java/org/apache/iceberg/V4TestHelpers.java
b/core/src/test/java/org/apache/iceberg/V4TestHelpers.java
index 1b922b576c..e9db4950bd 100644
--- a/core/src/test/java/org/apache/iceberg/V4TestHelpers.java
+++ b/core/src/test/java/org/apache/iceberg/V4TestHelpers.java
@@ -119,7 +119,6 @@ class V4TestHelpers {
return new TrackedFileStruct(
tracking,
content,
- FORMAT_VERSION_V4,
location,
FileFormat.fromFileName(location),
RECORD_COUNT,