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

etudenhoefner 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 dc217b09dd Core: Fix RESTFileScanTaskParser to handle empty delete 
file references list (#14568)
dc217b09dd is described below

commit dc217b09ddf7824c125d580f42501de36307e9cf
Author: ajreid21 <[email protected]>
AuthorDate: Wed Nov 12 10:03:12 2025 -0800

    Core: Fix RESTFileScanTaskParser to handle empty delete file references 
list (#14568)
---
 .../iceberg/rest/RESTFileScanTaskParser.java       |  4 +-
 .../responses/TestPlanTableScanResponseParser.java | 45 ++++++++++++++++++++++
 2 files changed, 47 insertions(+), 2 deletions(-)

diff --git 
a/core/src/main/java/org/apache/iceberg/rest/RESTFileScanTaskParser.java 
b/core/src/main/java/org/apache/iceberg/rest/RESTFileScanTaskParser.java
index 0ada9083ee..ccd2872e04 100644
--- a/core/src/main/java/org/apache/iceberg/rest/RESTFileScanTaskParser.java
+++ b/core/src/main/java/org/apache/iceberg/rest/RESTFileScanTaskParser.java
@@ -58,7 +58,7 @@ class RESTFileScanTaskParser {
     generator.writeStartObject();
     generator.writeFieldName(DATA_FILE);
     ContentFileParser.toJson(fileScanTask.file(), partitionSpec, generator);
-    if (deleteFileReferences != null) {
+    if (deleteFileReferences != null && !deleteFileReferences.isEmpty()) {
       JsonUtil.writeIntegerArray(DELETE_FILE_REFERENCES, deleteFileReferences, 
generator);
     }
 
@@ -87,7 +87,7 @@ class RESTFileScanTaskParser {
     if (jsonNode.has(DELETE_FILE_REFERENCES)) {
       List<Integer> indices = JsonUtil.getIntegerList(DELETE_FILE_REFERENCES, 
jsonNode);
       Preconditions.checkArgument(
-          Collections.max(indices) < allDeleteFiles.size(),
+          indices.isEmpty() || Collections.max(indices) < 
allDeleteFiles.size(),
           "Invalid delete file references: %s, expected indices < %s",
           indices,
           allDeleteFiles.size());
diff --git 
a/core/src/test/java/org/apache/iceberg/rest/responses/TestPlanTableScanResponseParser.java
 
b/core/src/test/java/org/apache/iceberg/rest/responses/TestPlanTableScanResponseParser.java
index c807bc4655..5ddedcacae 100644
--- 
a/core/src/test/java/org/apache/iceberg/rest/responses/TestPlanTableScanResponseParser.java
+++ 
b/core/src/test/java/org/apache/iceberg/rest/responses/TestPlanTableScanResponseParser.java
@@ -264,4 +264,49 @@ public class TestPlanTableScanResponseParser {
 
     
assertThat(PlanTableScanResponseParser.toJson(copyResponse)).isEqualTo(expectedToJson);
   }
+
+  @Test
+  public void roundTripSerdeWithoutDeleteFiles() {
+    ResidualEvaluator residualEvaluator =
+        ResidualEvaluator.of(SPEC, Expressions.equal("id", 1), true);
+    FileScanTask fileScanTask =
+        new BaseFileScanTask(
+            FILE_A,
+            new DeleteFile[] {},
+            SchemaParser.toJson(SCHEMA),
+            PartitionSpecParser.toJson(SPEC),
+            residualEvaluator);
+    PlanTableScanResponse response =
+        PlanTableScanResponse.builder()
+            .withPlanStatus(PlanStatus.COMPLETED)
+            .withFileScanTasks(List.of(fileScanTask))
+            .withSpecsById(PARTITION_SPECS_BY_ID)
+            .build();
+
+    String expectedJson =
+        "{\"plan-status\":\"completed\","
+            + "\"file-scan-tasks\":["
+            + 
"{\"data-file\":{\"spec-id\":0,\"content\":\"DATA\",\"file-path\":\"/path/to/data-a.parquet\","
+            + "\"file-format\":\"PARQUET\",\"partition\":{\"1000\":0},"
+            + 
"\"file-size-in-bytes\":10,\"record-count\":1,\"sort-order-id\":0},"
+            + 
"\"residual-filter\":{\"type\":\"eq\",\"term\":\"id\",\"value\":1}}]"
+            + "}";
+
+    String json = PlanTableScanResponseParser.toJson(response);
+    assertThat(json).isEqualTo(expectedJson);
+
+    PlanTableScanResponse fromResponse =
+        PlanTableScanResponseParser.fromJson(json, PARTITION_SPECS_BY_ID, 
false);
+    PlanTableScanResponse copyResponse =
+        PlanTableScanResponse.builder()
+            .withPlanStatus(fromResponse.planStatus())
+            .withPlanId(fromResponse.planId())
+            .withPlanTasks(fromResponse.planTasks())
+            .withDeleteFiles(fromResponse.deleteFiles())
+            .withFileScanTasks(fromResponse.fileScanTasks())
+            .withSpecsById(PARTITION_SPECS_BY_ID)
+            .build();
+
+    
assertThat(PlanTableScanResponseParser.toJson(copyResponse)).isEqualTo(expectedJson);
+  }
 }

Reply via email to