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

yiguolei pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.1 by this push:
     new f24337930c8 branch-4.1: [fix](iceberg) Fix execute action validation 
gaps #61381 (#61747)
f24337930c8 is described below

commit f24337930c8a62141c1fc900f2afbaa73c6d6084
Author: Socrates <[email protected]>
AuthorDate: Thu Mar 26 18:02:31 2026 +0800

    branch-4.1: [fix](iceberg) Fix execute action validation gaps #61381 
(#61747)
    
    Cherry-pick #61381 to branch-4.1
    
    ### What problem does this PR solve?
    
    - Related PR: #61381
    
    Fix Iceberg execute action validation gaps so `rollback_to_timestamp`
    correctly parses epoch millis input, and reject invalid
    `rewrite_data_files` input when `min-file-size-bytes >
    max-file-size-bytes` during FE validation.
    
    ### Cherry-pick commit
    
    - `ca001776658` - [fix](iceberg) Fix execute action validation gaps
    (#61381)
---
 .../action/IcebergRewriteDataFilesAction.java      |  3 +++
 .../action/IcebergRollbackToTimestampAction.java   | 21 ++++++++++++++-
 .../action/test_iceberg_execute_actions.groovy     | 30 ++++++++++++++++++++++
 3 files changed, 53 insertions(+), 1 deletion(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/action/IcebergRewriteDataFilesAction.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/action/IcebergRewriteDataFilesAction.java
index 2d9ee4b1377..a22397a146b 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/action/IcebergRewriteDataFilesAction.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/action/IcebergRewriteDataFilesAction.java
@@ -161,6 +161,9 @@ public class IcebergRewriteDataFilesAction extends 
BaseIcebergAction {
         if (this.maxFileSizeBytes == 0) {
             this.maxFileSizeBytes = (long) (targetFileSizeBytes * 1.8);
         }
+        if (this.minFileSizeBytes > this.maxFileSizeBytes) {
+            throw new UserException("min-file-size-bytes must be less than or 
equal to max-file-size-bytes");
+        }
         validateNoPartitions();
     }
 
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/action/IcebergRollbackToTimestampAction.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/action/IcebergRollbackToTimestampAction.java
index 0f50364920c..6957c563512 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/action/IcebergRollbackToTimestampAction.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/action/IcebergRollbackToTimestampAction.java
@@ -36,6 +36,7 @@ import java.time.format.DateTimeFormatter;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.TimeZone;
 
 /**
  * Iceberg rollback to timestamp action implementation.
@@ -103,7 +104,7 @@ public class IcebergRollbackToTimestampAction extends 
BaseIcebergAction {
         Long previousSnapshotId = previousSnapshot != null ? 
previousSnapshot.snapshotId() : null;
 
         try {
-            long targetTimestamp = TimeUtils.msTimeStringToLong(timestampStr, 
TimeUtils.getTimeZone());
+            long targetTimestamp = parseTimestampMillis(timestampStr, 
TimeUtils.getTimeZone());
             
icebergTable.manageSnapshots().rollbackToTime(targetTimestamp).commit();
 
             Snapshot currentSnapshot = icebergTable.currentSnapshot();
@@ -133,4 +134,22 @@ public class IcebergRollbackToTimestampAction extends 
BaseIcebergAction {
     public String getDescription() {
         return "Rollback Iceberg table to the snapshot that was current at a 
specific timestamp";
     }
+
+    static long parseTimestampMillis(String timestampStr, TimeZone timeZone) {
+        String trimmed = timestampStr.trim();
+        try {
+            long timestampMs = Long.parseLong(trimmed);
+            if (timestampMs < 0) {
+                throw new IllegalArgumentException("Timestamp must be 
non-negative: " + timestampMs);
+            }
+            return timestampMs;
+        } catch (NumberFormatException e) {
+            long parsedTimestamp = TimeUtils.msTimeStringToLong(trimmed, 
timeZone);
+            if (parsedTimestamp < 0) {
+                throw new IllegalArgumentException("Invalid timestamp format. 
Expected ISO datetime "
+                        + "(yyyy-MM-dd HH:mm:ss.SSS) or timestamp in 
milliseconds: " + trimmed, e);
+            }
+            return parsedTimestamp;
+        }
+    }
 }
diff --git 
a/regression-test/suites/external_table_p0/iceberg/action/test_iceberg_execute_actions.groovy
 
b/regression-test/suites/external_table_p0/iceberg/action/test_iceberg_execute_actions.groovy
index f305afd55ff..bf8697aaefd 100644
--- 
a/regression-test/suites/external_table_p0/iceberg/action/test_iceberg_execute_actions.groovy
+++ 
b/regression-test/suites/external_table_p0/iceberg/action/test_iceberg_execute_actions.groovy
@@ -264,6 +264,23 @@ suite("test_iceberg_optimize_actions_ddl", 
"p0,external,doris,external_docker,ex
     logger.info("Rollback timestamp result: ${rollbackTimestampResult}")
     qt_after_rollback_to_timestamp """SELECT * FROM test_rollback_timestamp 
ORDER BY id"""
 
+    String epochMillisSnapshotTime = String.valueOf(
+            dateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli())
+
+    List<List<Object>> rollbackTimestampEpochResult = sql """
+        ALTER TABLE ${catalog_name}.${db_name}.test_rollback_timestamp
+        EXECUTE rollback_to_timestamp("timestamp" = 
"${epochMillisSnapshotTime}")
+    """
+    logger.info("Rollback epoch millis result: 
${rollbackTimestampEpochResult}")
+
+    List<List<Object>> rowsAfterEpochRollback = sql """
+        SELECT id, version FROM test_rollback_timestamp ORDER BY id
+    """
+    assertTrue(rowsAfterEpochRollback.size() == 2,
+            "Expected rollback_to_timestamp with epoch millis to keep exactly 
2 rows")
+    assertTrue(rowsAfterEpochRollback[0][0] == 1 && 
rowsAfterEpochRollback[1][0] == 2,
+            "Expected rollback_to_timestamp with epoch millis to restore the 
first two snapshots")
+
 
     // 
=====================================================================================
     // Test Case 3: set_current_snapshot action
@@ -484,6 +501,19 @@ suite("test_iceberg_optimize_actions_ddl", 
"p0,external,doris,external_docker,ex
         exception "Invalid target-file-size-bytes format: not-a-number"
     }
 
+    // Test rewrite_data_files with invalid min/max file size relationship
+    test {
+        sql """
+            ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE 
rewrite_data_files
+            (
+                "target-file-size-bytes" = "536870912",
+                "min-file-size-bytes" = "1073741824",
+                "max-file-size-bytes" = "536870912"
+            )
+        """
+        exception "min-file-size-bytes must be less than or equal to 
max-file-size-bytes"
+    }
+
     // Test set_current_snapshot with both snapshot_id and ref
     test {
         sql """


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to