1#[cfg(feature = "sqlx-dep")]
2pub use sqlx::error::Error as SqlxError;
3
4#[cfg(feature = "sqlx-mysql")]
5pub use sqlx::mysql::MySqlDatabaseError as SqlxMySqlError;
6
7#[cfg(feature = "sqlx-postgres")]
8pub use sqlx::postgres::PgDatabaseError as SqlxPostgresError;
9
10#[cfg(feature = "sqlx-sqlite")]
11pub use sqlx::sqlite::SqliteError as SqlxSqliteError;
12
13use std::sync::Arc;
14use thiserror::Error;
15
16#[derive(Error, Debug, Clone)]
18pub enum DbErr {
19 #[error("Failed to acquire connection from pool: {0}")]
21 ConnectionAcquire(#[source] ConnAcquireErr),
22 #[error("Error converting `{from}` into `{into}`: {source}")]
24 TryIntoErr {
25 from: &'static str,
27 into: &'static str,
29 source: Arc<dyn std::error::Error + Send + Sync>,
31 },
32 #[error("Connection Error: {0}")]
34 Conn(#[source] RuntimeErr),
35 #[error("Execution Error: {0}")]
37 Exec(#[source] RuntimeErr),
38 #[error("Query Error: {0}")]
40 Query(#[source] RuntimeErr),
41 #[error("Type '{0}' cannot be converted from u64")]
43 ConvertFromU64(&'static str),
44 #[error("Failed to unpack last_insert_id")]
46 UnpackInsertId,
47 #[error("Failed to get primary key from model")]
50 UpdateGetPrimaryKey,
51 #[error("RecordNotFound Error: {0}")]
53 RecordNotFound(String),
54 #[error("Attribute {0} is NotSet")]
56 AttrNotSet(String),
57 #[error("Custom Error: {0}")]
59 Custom(String),
60 #[error("Type Error: {0}")]
62 Type(String),
63 #[error("Json Error: {0}")]
65 Json(String),
66 #[error("Migration Error: {0}")]
68 Migration(String),
69 #[error("None of the records are inserted")]
72 RecordNotInserted,
73 #[error("None of the records are updated")]
76 RecordNotUpdated,
77 #[error("Operation not supported by backend {db}: {ctx}")]
79 BackendNotSupported {
80 db: &'static str,
82 ctx: &'static str,
84 },
85 #[error("Key arity mismatch: expected {expected}, received {received}")]
87 KeyArityMismatch {
88 expected: u8,
90 received: u8,
92 },
93 #[error("Primay key not set for {ctx}")]
95 PrimaryKeyNotSet {
96 ctx: &'static str,
98 },
99 #[error("RBAC error: {0}")]
101 RbacError(String),
102 #[error("Access denied: cannot perform `{permission}` on `{resource}`")]
104 AccessDenied {
105 permission: String,
107 resource: String,
109 },
110 #[error("Mutex poisoned")]
112 MutexPoisonError,
113}
114
115#[derive(Debug)]
117pub enum TryGetError {
118 DbErr(DbErr),
120 Null(String),
122}
123
124#[derive(Error, Debug, PartialEq, Eq, Copy, Clone)]
126pub enum ConnAcquireErr {
127 #[error("Connection pool timed out")]
129 Timeout,
130 #[error("Connection closed")]
132 ConnectionClosed,
133}
134
135#[derive(Error, Debug, Clone)]
137pub enum RuntimeErr {
138 #[cfg(feature = "sqlx-dep")]
140 #[error("{0}")]
141 SqlxError(Arc<sqlx::error::Error>),
142 #[cfg(feature = "rusqlite")]
144 #[error("{0}")]
145 Rusqlite(Arc<crate::driver::rusqlite::RusqliteError>),
146 #[error("{0}")]
148 Internal(String),
149}
150
151impl PartialEq for DbErr {
152 fn eq(&self, other: &Self) -> bool {
153 self.to_string() == other.to_string()
154 }
155}
156
157impl Eq for DbErr {}
158
159#[derive(Error, Debug)]
161#[error("Failed to match \"{0}\" as Column")]
162pub struct ColumnFromStrErr(pub String);
163
164#[allow(dead_code)]
165pub(crate) fn conn_err<T>(s: T) -> DbErr
166where
167 T: ToString,
168{
169 DbErr::Conn(RuntimeErr::Internal(s.to_string()))
170}
171
172#[allow(dead_code)]
173pub(crate) fn exec_err<T>(s: T) -> DbErr
174where
175 T: ToString,
176{
177 DbErr::Exec(RuntimeErr::Internal(s.to_string()))
178}
179
180#[allow(dead_code)]
181pub(crate) fn query_err<T>(s: T) -> DbErr
182where
183 T: ToString,
184{
185 DbErr::Query(RuntimeErr::Internal(s.to_string()))
186}
187
188#[allow(dead_code)]
189pub(crate) fn type_err<T>(s: T) -> DbErr
190where
191 T: ToString,
192{
193 DbErr::Type(s.to_string())
194}
195
196#[allow(dead_code)]
197pub(crate) fn json_err<T>(s: T) -> DbErr
198where
199 T: ToString,
200{
201 DbErr::Json(s.to_string())
202}
203
204#[derive(Error, Debug, Clone, PartialEq, Eq)]
206#[non_exhaustive]
207pub enum SqlErr {
208 #[error("Unique Constraint Violated: {0}")]
210 UniqueConstraintViolation(String),
211 #[error("Foreign Key Constraint Violated: {0}")]
213 ForeignKeyConstraintViolation(String),
214}
215
216#[allow(dead_code)]
217impl DbErr {
218 pub fn sql_err(&self) -> Option<SqlErr> {
220 #[cfg(any(
221 feature = "sqlx-mysql",
222 feature = "sqlx-postgres",
223 feature = "sqlx-sqlite"
224 ))]
225 {
226 use std::ops::Deref;
227 if let DbErr::Exec(RuntimeErr::SqlxError(e)) | DbErr::Query(RuntimeErr::SqlxError(e)) =
228 self
229 && let sqlx::Error::Database(e) = e.deref()
230 {
231 let error_code = e.code().unwrap_or_default();
232 let _error_code_expanded = error_code.deref();
233 #[cfg(feature = "sqlx-mysql")]
234 if e.try_downcast_ref::<sqlx::mysql::MySqlDatabaseError>()
235 .is_some()
236 {
237 let error_number = e
238 .try_downcast_ref::<sqlx::mysql::MySqlDatabaseError>()?
239 .number();
240 match error_number {
241 1022 | 1062 | 1169 | 1586 => {
246 return Some(SqlErr::UniqueConstraintViolation(e.message().into()));
247 }
248 1216 | 1217 | 1451 | 1452 | 1557 | 1761 | 1762 => {
256 return Some(SqlErr::ForeignKeyConstraintViolation(e.message().into()));
257 }
258 _ => return None,
259 }
260 }
261 #[cfg(feature = "sqlx-postgres")]
262 if e.try_downcast_ref::<sqlx::postgres::PgDatabaseError>()
263 .is_some()
264 {
265 match _error_code_expanded {
266 "23505" => {
267 return Some(SqlErr::UniqueConstraintViolation(e.message().into()));
268 }
269 "23503" => {
270 return Some(SqlErr::ForeignKeyConstraintViolation(e.message().into()));
271 }
272 _ => return None,
273 }
274 }
275 #[cfg(feature = "sqlx-sqlite")]
276 if e.try_downcast_ref::<sqlx::sqlite::SqliteError>().is_some() {
277 match _error_code_expanded {
278 "1555" | "2067" => {
281 return Some(SqlErr::UniqueConstraintViolation(e.message().into()));
282 }
283 "787" => {
284 return Some(SqlErr::ForeignKeyConstraintViolation(e.message().into()));
285 }
286 _ => return None,
287 }
288 }
289 }
290 }
291 #[cfg(feature = "rusqlite")]
292 if let DbErr::Exec(RuntimeErr::Rusqlite(err)) | DbErr::Query(RuntimeErr::Rusqlite(err)) =
293 self
294 {
295 use crate::driver::rusqlite::RusqliteError;
296 use std::ops::Deref;
297
298 if let RusqliteError::SqliteFailure(err, msg) = err.deref() {
299 match err.extended_code {
300 1555 | 2067 => {
301 return Some(SqlErr::UniqueConstraintViolation(
302 msg.to_owned().unwrap_or_else(|| err.to_string()),
303 ));
304 }
305 787 => {
306 return Some(SqlErr::ForeignKeyConstraintViolation(
307 msg.to_owned().unwrap_or_else(|| err.to_string()),
308 ));
309 }
310 _ => (),
311 }
312 }
313 }
314 None
315 }
316}