1use std::num::ParseIntError;
4
5use aws_credential_types::provider::error::CredentialsError;
6use aws_sdk_dynamodb::{
7 error::SdkError,
8 operation::{
9 create_table::CreateTableError, delete_item::DeleteItemError, get_item::GetItemError,
10 put_item::PutItemError, query::QueryError, update_item::UpdateItemError,
11 },
12};
13use aws_smithy_runtime_api::client::result::ServiceError;
14use deltalake_core::kernel::Version;
15
16macro_rules! impl_from_service_error {
17 ($error_type:ty) => {
18 impl<R> From<SdkError<$error_type, R>> for LockClientError
19 where
20 R: Send + Sync + std::fmt::Debug + 'static,
21 {
22 fn from(err: SdkError<$error_type, R>) -> Self {
23 match err {
24 SdkError::ServiceError(e) => e.into(),
25 _ => LockClientError::GenericDynamoDb {
26 source: Box::new(err),
27 },
28 }
29 }
30 }
31
32 impl<R> From<ServiceError<$error_type, R>> for LockClientError
33 where
34 R: Send + Sync + std::fmt::Debug + 'static,
35 {
36 fn from(value: ServiceError<$error_type, R>) -> Self {
37 value.into_err().into()
38 }
39 }
40 };
41}
42
43#[derive(thiserror::Error, Debug)]
44pub enum DynamoDbConfigError {
45 #[error("Invalid billing mode : {0}, supported values : ['provided', 'pay_per_request']")]
47 InvalidBillingMode(String),
48
49 #[error("Cannot parse max elapsed request time into u64: {source}")]
51 ParseMaxElapsedRequestTime {
52 source: ParseIntError,
54 },
55 #[error("Cannot initialize dynamodb lock configuration")]
57 InitializationError,
58}
59
60#[derive(thiserror::Error, Debug)]
62pub enum LockClientError {
63 #[error("Log item has invalid content: '{description}'")]
64 InconsistentData { description: String },
65
66 #[error("Lock table '{name}': creation failed: {source}")]
67 LockTableCreateFailure {
68 name: String,
69 source: Box<CreateTableError>,
70 },
71
72 #[error("Log entry for table '{table_path}' and version '{version}' already exists")]
73 VersionAlreadyExists {
74 table_path: String,
75 version: Version,
76 },
77
78 #[error("Provisioned table throughput exceeded")]
79 ProvisionedThroughputExceeded,
80
81 #[error("Lock table not found")]
82 LockTableNotFound,
83
84 #[error("error in DynamoDb")]
85 GenericDynamoDb {
86 source: Box<dyn std::error::Error + Send + Sync + 'static>,
87 },
88 #[error("configuration error: {source}")]
89 Credentials { source: CredentialsError },
90 #[error(
91 "Atomic rename requires a LockClient for S3 backends. \
92 Either configure the LockClient, or set AWS_S3_ALLOW_UNSAFE_RENAME=true \
93 to opt out of support for concurrent writers."
94 )]
95 LockClientRequired,
96
97 #[error("Log entry for table '{table_path}' and version '{version}' is already complete")]
98 VersionAlreadyCompleted {
99 table_path: String,
100 version: Version,
101 },
102}
103
104impl From<GetItemError> for LockClientError {
105 fn from(err: GetItemError) -> Self {
106 match err {
107 GetItemError::ProvisionedThroughputExceededException(_) => {
108 LockClientError::ProvisionedThroughputExceeded
109 }
110 GetItemError::RequestLimitExceeded(_) => LockClientError::ProvisionedThroughputExceeded,
111 GetItemError::ResourceNotFoundException(_) => LockClientError::LockTableNotFound,
112 _ => LockClientError::GenericDynamoDb {
113 source: Box::new(err),
114 },
115 }
116 }
117}
118
119impl From<QueryError> for LockClientError {
120 fn from(err: QueryError) -> Self {
121 match err {
122 QueryError::ProvisionedThroughputExceededException(_) => {
123 LockClientError::ProvisionedThroughputExceeded
124 }
125 QueryError::RequestLimitExceeded(_) => LockClientError::ProvisionedThroughputExceeded,
126 QueryError::ResourceNotFoundException(_) => LockClientError::LockTableNotFound,
127 _ => LockClientError::GenericDynamoDb {
128 source: Box::new(err),
129 },
130 }
131 }
132}
133
134impl From<PutItemError> for LockClientError {
135 fn from(err: PutItemError) -> Self {
136 match err {
137 PutItemError::ConditionalCheckFailedException(_) => {
138 unreachable!("error must be handled explicitly")
139 }
140 PutItemError::ProvisionedThroughputExceededException(_) => {
141 LockClientError::ProvisionedThroughputExceeded
142 }
143 PutItemError::RequestLimitExceeded(_) => LockClientError::ProvisionedThroughputExceeded,
144 PutItemError::ResourceNotFoundException(_) => LockClientError::LockTableNotFound,
145 PutItemError::ItemCollectionSizeLimitExceededException(_) => err.into(),
146 PutItemError::TransactionConflictException(_) => err.into(),
147 _ => LockClientError::GenericDynamoDb {
148 source: Box::new(err),
149 },
150 }
151 }
152}
153
154impl From<UpdateItemError> for LockClientError {
155 fn from(err: UpdateItemError) -> Self {
156 match err {
157 UpdateItemError::ConditionalCheckFailedException(_) => {
158 unreachable!("condition check failure in update is not an error")
159 }
160 UpdateItemError::InternalServerError(_) => err.into(),
161 UpdateItemError::ProvisionedThroughputExceededException(_) => {
162 LockClientError::ProvisionedThroughputExceeded
163 }
164 UpdateItemError::RequestLimitExceeded(_) => {
165 LockClientError::ProvisionedThroughputExceeded
166 }
167 UpdateItemError::ResourceNotFoundException(_) => LockClientError::LockTableNotFound,
168 UpdateItemError::ItemCollectionSizeLimitExceededException(_) => err.into(),
169 UpdateItemError::TransactionConflictException(_) => err.into(),
170 _ => LockClientError::GenericDynamoDb {
171 source: Box::new(err),
172 },
173 }
174 }
175}
176
177impl From<DeleteItemError> for LockClientError {
178 fn from(err: DeleteItemError) -> Self {
179 match err {
180 DeleteItemError::ConditionalCheckFailedException(_) => {
181 unreachable!("error must be handled explicitly")
182 }
183 DeleteItemError::InternalServerError(_) => err.into(),
184 DeleteItemError::ProvisionedThroughputExceededException(_) => {
185 LockClientError::ProvisionedThroughputExceeded
186 }
187 DeleteItemError::RequestLimitExceeded(_) => {
188 LockClientError::ProvisionedThroughputExceeded
189 }
190 DeleteItemError::ResourceNotFoundException(_) => LockClientError::LockTableNotFound,
191 DeleteItemError::ItemCollectionSizeLimitExceededException(_) => err.into(),
192 DeleteItemError::TransactionConflictException(_) => err.into(),
193 _ => LockClientError::GenericDynamoDb {
194 source: Box::new(err),
195 },
196 }
197 }
198}
199
200impl_from_service_error!(GetItemError);
201impl_from_service_error!(PutItemError);
202impl_from_service_error!(QueryError);
203impl_from_service_error!(UpdateItemError);
204impl_from_service_error!(DeleteItemError);