diff --git a/crates/iceberg/src/error.rs b/crates/iceberg/src/error.rs index 1eef1bcc4..88410826f 100644 --- a/crates/iceberg/src/error.rs +++ b/crates/iceberg/src/error.rs @@ -28,6 +28,9 @@ pub type Result = std::result::Result; #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[non_exhaustive] pub enum ErrorKind { + /// The operation was rejected because the system is not in a state required for the operation’s execution. + PreconditionFailed, + /// Iceberg don't know what happened here, and no actions other than /// just returning it back. For example, iceberg returns an internal /// service error. @@ -76,6 +79,7 @@ impl From for &'static str { ErrorKind::TableNotFound => "TableNotFound", ErrorKind::NamespaceAlreadyExists => "NamespaceAlreadyExists", ErrorKind::NamespaceNotFound => "NamespaceNotFound", + ErrorKind::PreconditionFailed => "PreconditionFailed", } } } diff --git a/crates/iceberg/src/transaction/append.rs b/crates/iceberg/src/transaction/append.rs index 474b32bec..d3960362f 100644 --- a/crates/iceberg/src/transaction/append.rs +++ b/crates/iceberg/src/transaction/append.rs @@ -219,6 +219,15 @@ mod tests { use crate::transaction::Transaction; use crate::{TableRequirement, TableUpdate}; + #[tokio::test] + async fn test_empty_data_append_action() { + let table = make_v2_minimal_table(); + let tx = Transaction::new(&table); + let mut action = tx.fast_append(None, vec![]).unwrap(); + action.add_data_files(vec![]).unwrap(); + assert!(action.apply().await.is_err()); + } + #[tokio::test] async fn test_fast_append_action() { let table = make_v2_minimal_table(); diff --git a/crates/iceberg/src/transaction/snapshot.rs b/crates/iceberg/src/transaction/snapshot.rs index ee9721c16..0bded9135 100644 --- a/crates/iceberg/src/transaction/snapshot.rs +++ b/crates/iceberg/src/transaction/snapshot.rs @@ -172,6 +172,13 @@ impl<'a> SnapshotProduceAction<'a> { // Write manifest file for added data files and return the ManifestFile for ManifestList. async fn write_added_manifest(&mut self) -> Result { let added_data_files = std::mem::take(&mut self.added_data_files); + if added_data_files.is_empty() { + return Err(Error::new( + ErrorKind::PreconditionFailed, + "No added data files found when write a manifest file", + )); + } + let snapshot_id = self.snapshot_id; let format_version = self.tx.current_table.metadata().format_version(); let manifest_entries = added_data_files.into_iter().map(|data_file| {