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]