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 {