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

pvary 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 417985375c Core: Fix ArrayIndexOutOfBoundsException in 
RewriteTablePathUtil (#17629)
417985375c is described below

commit 417985375c69da1bbcf22de6e9c89a8bd01a68ab
Author: Yuya Ebihara <[email protected]>
AuthorDate: Fri Oct 16 06:04:48 2026 +0900

    Core: Fix ArrayIndexOutOfBoundsException in RewriteTablePathUtil (#17629)
---
 .../org/apache/iceberg/RewriteTablePathUtil.java   |  2 +-
 .../apache/iceberg/TestRewriteTablePathUtil.java   | 89 ++++++++++++++++++++++
 2 files changed, 90 insertions(+), 1 deletion(-)

diff --git a/core/src/main/java/org/apache/iceberg/RewriteTablePathUtil.java 
b/core/src/main/java/org/apache/iceberg/RewriteTablePathUtil.java
index f685680d43..e1afe80d66 100644
--- a/core/src/main/java/org/apache/iceberg/RewriteTablePathUtil.java
+++ b/core/src/main/java/org/apache/iceberg/RewriteTablePathUtil.java
@@ -774,7 +774,7 @@ public class RewriteTablePathUtil {
 
   private static void checkNoRowData(Record record, String deleteFilePath) {
     Preconditions.checkArgument(
-        record.get(2) == null,
+        record.size() == 2 || record.get(2) == null,
         "Cannot rewrite position delete file with row data for %s",
         deleteFilePath);
   }
diff --git 
a/core/src/test/java/org/apache/iceberg/TestRewriteTablePathUtil.java 
b/core/src/test/java/org/apache/iceberg/TestRewriteTablePathUtil.java
index 627a9fd957..64429dc3c1 100644
--- a/core/src/test/java/org/apache/iceberg/TestRewriteTablePathUtil.java
+++ b/core/src/test/java/org/apache/iceberg/TestRewriteTablePathUtil.java
@@ -23,13 +23,18 @@ import static 
org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.assertj.core.api.Assertions.entry;
 import static org.assertj.core.api.Assumptions.assumeThat;
 
+import java.io.File;
 import java.io.IOException;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
+import org.apache.iceberg.avro.Avro;
 import org.apache.iceberg.data.GenericRecord;
 import org.apache.iceberg.data.Record;
+import org.apache.iceberg.data.avro.DataWriter;
+import org.apache.iceberg.data.avro.PlannedDataReader;
+import org.apache.iceberg.deletes.PositionDelete;
 import org.apache.iceberg.deletes.PositionDeleteWriter;
 import org.apache.iceberg.io.CloseableIterable;
 import org.apache.iceberg.io.DeleteSchemaUtil;
@@ -37,6 +42,7 @@ import org.apache.iceberg.io.InputFile;
 import org.apache.iceberg.io.OutputFile;
 import org.apache.iceberg.relocated.com.google.common.collect.ImmutableList;
 import org.apache.iceberg.relocated.com.google.common.collect.ImmutableMap;
+import org.apache.iceberg.relocated.com.google.common.collect.Lists;
 import org.apache.iceberg.util.Pair;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestTemplate;
@@ -610,6 +616,89 @@ public class TestRewriteTablePathUtil extends TestBase {
         .hasMessageContaining("Cannot rewrite position delete file with row 
data for");
   }
 
+  @Test
+  void rewritePositionDeleteWith2FieldReader() throws IOException {
+    File sourceFile =
+        new File(
+            FileFormat.AVRO.addExtension(
+                temp.resolve("source-pos-deletes-" + 
System.nanoTime()).toString()));
+    OutputFile sourceOutput = Files.localOutput(sourceFile);
+
+    String sourcePrefix = temp.toAbsolutePath().toString();
+    String targetPrefix = temp.resolve("target").toAbsolutePath().toString();
+    String dataFilePath = sourcePrefix + "/data/file.parquet";
+
+    try (PositionDeleteWriter<Void> writer =
+        Avro.writeDeletes(sourceOutput)
+            .createWriterFunc(DataWriter::create)
+            .overwrite()
+            .withSpec(PartitionSpec.unpartitioned())
+            .buildPositionWriter()) {
+      writer.write(PositionDelete.<Void>create().set(dataFilePath, 0L));
+    }
+
+    OutputFile targetOutput =
+        Files.localOutput(
+            FileFormat.AVRO.addExtension(
+                temp.resolve("target-pos-deletes-" + 
System.nanoTime()).toString()));
+
+    // Reader that projects only 2 fields (file_path + pos), simulating an 
impl that omits row data
+    RewriteTablePathUtil.PositionDeleteReaderWriter twoFieldReaderWriter =
+        new RewriteTablePathUtil.PositionDeleteReaderWriter() {
+          @Override
+          public CloseableIterable<Record> reader(
+              InputFile inputFile, FileFormat format, PartitionSpec spec) {
+            return Avro.read(inputFile)
+                .project(DeleteSchemaUtil.pathPosSchema())
+                .createResolvingReader(PlannedDataReader::create)
+                .build();
+          }
+
+          @Override
+          public PositionDeleteWriter<Record> writer(
+              OutputFile outputFile, FileFormat format, PartitionSpec spec, 
StructLike partition)
+              throws IOException {
+            return Avro.writeDeletes(outputFile)
+                .createWriterFunc(DataWriter::create)
+                .overwrite()
+                .withSpec(spec)
+                .buildPositionWriter();
+          }
+        };
+
+    DeleteFile deleteFile =
+        FileMetadata.deleteFileBuilder(PartitionSpec.unpartitioned())
+            .ofPositionDeletes()
+            .withFormat(FileFormat.AVRO)
+            .withPath(sourceFile.getAbsolutePath())
+            .withFileSizeInBytes(sourceFile.length())
+            .withRecordCount(1)
+            .build();
+
+    RewriteTablePathUtil.rewritePositionDelete(
+        deleteFile,
+        targetOutput,
+        table.io(),
+        PartitionSpec.unpartitioned(),
+        sourcePrefix,
+        targetPrefix,
+        twoFieldReaderWriter);
+
+    List<Record> records = Lists.newArrayList();
+    try (CloseableIterable<Record> reader =
+        Avro.read(targetOutput.toInputFile())
+            .project(DeleteSchemaUtil.pathPosSchema())
+            .createResolvingReader(PlannedDataReader::create)
+            .build()) {
+      reader.forEach(records::add);
+    }
+
+    assertThat(records).hasSize(1);
+    Record record = records.get(0);
+    
assertThat(record.get(0)).asString().startsWith(targetPrefix).endsWith("data/file.parquet");
+    assertThat(record.get(1)).isEqualTo(0L);
+  }
+
   /** Returns one position delete record that carries row data, as 1.11 and 
earlier could write. */
   private static class RowCarryingReaderWriter
       implements RewriteTablePathUtil.PositionDeleteReaderWriter {

Reply via email to