You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iceberg.apache.org by am...@apache.org on 2026/04/29 03:11:43 UTC
(iceberg) branch main updated: Core: Validate v2 deletes against concurrent format upgrade (#16146)
This is an automated email from the ASF dual-hosted git repository.
amogh-jahagirdar 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 099ef477c8 Core: Validate v2 deletes against concurrent format upgrade (#16146)
099ef477c8 is described below
commit 099ef477c8024b3bcb4d12abca18944c25b0eb45
Author: Hongyue/Steve Zhang <ho...@gmail.com>
AuthorDate: Tue Apr 28 20:11:36 2026 -0700
Core: Validate v2 deletes against concurrent format upgrade (#16146)
* Core: validate buffered v2 deletes against concurrent format upgrade
* rename to validateDeleteFilesForVersion
---
.../org/apache/iceberg/MergingSnapshotProducer.java | 18 +++++++++++++++---
.../test/java/org/apache/iceberg/TestRowDelta.java | 21 +++++++++++++++++++++
2 files changed, 36 insertions(+), 3 deletions(-)
diff --git a/core/src/main/java/org/apache/iceberg/MergingSnapshotProducer.java b/core/src/main/java/org/apache/iceberg/MergingSnapshotProducer.java
index eed4e56dc0..e072382543 100644
--- a/core/src/main/java/org/apache/iceberg/MergingSnapshotProducer.java
+++ b/core/src/main/java/org/apache/iceberg/MergingSnapshotProducer.java
@@ -289,7 +289,11 @@ abstract class MergingSnapshotProducer<ThisT> extends SnapshotProducer<ThisT> {
protected void validateNewDeleteFile(DeleteFile file) {
Preconditions.checkNotNull(file, "Invalid delete file: null");
- switch (formatVersion()) {
+ validateDeleteFileForVersion(file, formatVersion());
+ }
+
+ private static void validateDeleteFileForVersion(DeleteFile file, int formatVersion) {
+ switch (formatVersion) {
case 1:
throw new IllegalArgumentException("Deletes are supported in V2 and above");
case 2:
@@ -303,11 +307,11 @@ abstract class MergingSnapshotProducer<ThisT> extends SnapshotProducer<ThisT> {
Preconditions.checkArgument(
file.content() == FileContent.EQUALITY_DELETES || ContentFileUtil.isDV(file),
"Must use DVs for position deletes in V%s: %s",
- formatVersion(),
+ formatVersion,
file.location());
break;
default:
- throw new IllegalArgumentException("Unsupported format version: " + formatVersion());
+ throw new IllegalArgumentException("Unsupported format version: " + formatVersion);
}
}
@@ -959,8 +963,16 @@ abstract class MergingSnapshotProducer<ThisT> extends SnapshotProducer<ThisT> {
return summaryBuilder.build();
}
+ // guard buffered deletes against concurrent format upgrade
+ private void validateDeleteFilesForVersion(int currentFormatVersion) {
+ for (DeleteFile file : v2Deletes) {
+ validateDeleteFileForVersion(file, currentFormatVersion);
+ }
+ }
+
@Override
public List<ManifestFile> apply(TableMetadata base, Snapshot snapshot) {
+ validateDeleteFilesForVersion(base.formatVersion());
// filter any existing manifests
List<ManifestFile> filtered =
filterManager.filterManifests(
diff --git a/core/src/test/java/org/apache/iceberg/TestRowDelta.java b/core/src/test/java/org/apache/iceberg/TestRowDelta.java
index c442541289..aaccf41224 100644
--- a/core/src/test/java/org/apache/iceberg/TestRowDelta.java
+++ b/core/src/test/java/org/apache/iceberg/TestRowDelta.java
@@ -2419,6 +2419,27 @@ public class TestRowDelta extends TestBase {
assertThat(taskDV.contentSizeInBytes()).isEqualTo(dv.contentSizeInBytes());
}
+ @TestTemplate
+ public void testV2StagedPositionDeleteCannotCommitToV3() {
+ assumeThat(formatVersion).isEqualTo(2);
+
+ Snapshot initial = commit(table, table.newAppend().appendFile(FILE_A), branch);
+
+ // Stage RowDelta at v2: position delete for FILE_A + add new data FILE_B.
+ RowDelta rowDelta = table.newRowDelta().addDeletes(FILE_A_DELETES).addRows(FILE_B);
+
+ // upgrade the table
+ table.updateProperties().set(TableProperties.FORMAT_VERSION, "3").commit();
+
+ assertThatThrownBy(rowDelta::commit)
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageContaining("Must use DVs for position deletes in V3");
+
+ table.refresh();
+ assertThat(table.operations().current().formatVersion()).isEqualTo(3);
+ assertThat(table.snapshot(branch)).isEqualTo(initial);
+ }
+
@TestTemplate
public void testInabilityToAddPositionDeleteFilesInTablesWithDVs() {
assumeThat(formatVersion).isGreaterThanOrEqualTo(3);