sea_orm/
error.rs

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/// An error from unsuccessful database operations
17#[derive(Error, Debug, Clone)]
18pub enum DbErr {
19    /// This error can happen when the connection pool is fully-utilized
20    #[error("Failed to acquire connection from pool: {0}")]
21    ConnectionAcquire(#[source] ConnAcquireErr),
22    /// Runtime type conversion error
23    #[error("Error converting `{from}` into `{into}`: {source}")]
24    TryIntoErr {
25        /// From type
26        from: &'static str,
27        /// Into type
28        into: &'static str,
29        /// TryError
30        source: Arc<dyn std::error::Error>,
31    },
32    /// There was a problem with the database connection
33    #[error("Connection Error: {0}")]
34    Conn(#[source] RuntimeErr),
35    /// An operation did not execute successfully
36    #[error("Execution Error: {0}")]
37    Exec(#[source] RuntimeErr),
38    /// An error occurred while performing a query
39    #[error("Query Error: {0}")]
40    Query(#[source] RuntimeErr),
41    /// Type error: the specified type cannot be converted from u64. This is not a runtime error.
42    #[error("Type '{0}' cannot be converted from u64")]
43    ConvertFromU64(&'static str),
44    /// After an insert statement it was impossible to retrieve the last_insert_id
45    #[error("Failed to unpack last_insert_id")]
46    UnpackInsertId,
47    /// When updating, a model should know its primary key to check
48    /// if the record has been correctly updated, otherwise this error will occur
49    #[error("Failed to get primary key from model")]
50    UpdateGetPrimaryKey,
51    /// The record was not found in the database
52    #[error("RecordNotFound Error: {0}")]
53    RecordNotFound(String),
54    /// Thrown by `TryFrom<ActiveModel>`, which assumes all attributes are set/unchanged
55    #[error("Attribute {0} is NotSet")]
56    AttrNotSet(String),
57    /// A custom error
58    #[error("Custom Error: {0}")]
59    Custom(String),
60    /// Error occurred while parsing value as target type
61    #[error("Type Error: {0}")]
62    Type(String),
63    /// Error occurred while parsing json value as target type
64    #[error("Json Error: {0}")]
65    Json(String),
66    /// A migration error
67    #[error("Migration Error: {0}")]
68    Migration(String),
69    /// None of the records are inserted,
70    /// that probably means all of them conflict with existing records in the table
71    #[error("None of the records are inserted")]
72    RecordNotInserted,
73    /// None of the records are updated, that means a WHERE condition has no matches.
74    /// May be the table is empty or the record does not exist
75    #[error("None of the records are updated")]
76    RecordNotUpdated,
77    /// This operation is not supported by the database backend
78    #[error("Operation not supported by backend {db}: {ctx}")]
79    BackendNotSupported {
80        /// Database backend
81        db: &'static str,
82        /// Context
83        ctx: &'static str,
84    },
85    /// (Primary) Key arity mismatch
86    #[error("Key arity mismatch: expected {expected}, received {received}")]
87    KeyArityMismatch {
88        /// Expected value
89        expected: u8,
90        /// Received value
91        received: u8,
92    },
93    /// Primay key not set for update / delete
94    #[error("Primay key not set for {ctx}")]
95    PrimaryKeyNotSet {
96        /// Context
97        ctx: &'static str,
98    },
99    /// Error while running RBAC checks
100    #[error("RBAC error: {0}")]
101    RbacError(String),
102    /// Access denied after running RBAC checks
103    #[error("Access denied: cannot perform `{permission}` on `{resource}`")]
104    AccessDenied {
105        /// The required permission
106        permission: String,
107        /// The requested resource
108        resource: String,
109    },
110    /// Mutex was poisoned by another thread
111    #[error("Mutex poisoned")]
112    MutexPoisonError,
113}
114
115/// An error from trying to get a row from a Model
116#[derive(Debug)]
117pub enum TryGetError {
118    /// A database error was encountered as defined in [crate::DbErr]
119    DbErr(DbErr),
120    /// A null value was encountered
121    Null(String),
122}
123
124/// Connection Acquire error
125#[derive(Error, Debug, PartialEq, Eq, Copy, Clone)]
126pub enum ConnAcquireErr {
127    /// Connection pool timed out
128    #[error("Connection pool timed out")]
129    Timeout,
130    /// Connection closed
131    #[error("Connection closed")]
132    ConnectionClosed,
133}
134
135/// Runtime error
136#[derive(Error, Debug, Clone)]
137pub enum RuntimeErr {
138    /// SQLx Error
139    #[cfg(feature = "sqlx-dep")]
140    #[error("{0}")]
141    SqlxError(Arc<sqlx::error::Error>),
142    /// Rusqlite Error
143    #[cfg(feature = "rusqlite")]
144    #[error("{0}")]
145    Rusqlite(Arc<crate::driver::rusqlite::RusqliteError>),
146    /// Error generated from within SeaORM
147    #[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/// Error during `impl FromStr for Entity::Column`
160#[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/// An error from unsuccessful SQL query
205#[derive(Error, Debug, Clone, PartialEq, Eq)]
206#[non_exhaustive]
207pub enum SqlErr {
208    /// Error for duplicate record in unique field or primary key field
209    #[error("Unique Constraint Violated: {0}")]
210    UniqueConstraintViolation(String),
211    /// Error for Foreign key constraint
212    #[error("Foreign Key Constraint Violated: {0}")]
213    ForeignKeyConstraintViolation(String),
214}
215
216#[allow(dead_code)]
217impl DbErr {
218    /// Convert generic DbErr by sqlx to SqlErr, return none if the error is not any type of SqlErr
219    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            {
230                if let sqlx::Error::Database(e) = e.deref() {
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 Can't write; duplicate key in table '%s'
242                            // 1062 Duplicate entry '%s' for key %d
243                            // 1169 Can't write, because of unique constraint, to table '%s'
244                            // 1586 Duplicate entry '%s' for key '%s'
245                            1022 | 1062 | 1169 | 1586 => {
246                                return Some(SqlErr::UniqueConstraintViolation(e.message().into()));
247                            }
248                            // 1216 Cannot add or update a child row: a foreign key constraint fails
249                            // 1217 Cannot delete or update a parent row: a foreign key constraint fails
250                            // 1451 Cannot delete or update a parent row: a foreign key constraint fails (%s)
251                            // 1452 Cannot add or update a child row: a foreign key constraint fails (%s)
252                            // 1557 Upholding foreign key constraints for table '%s', entry '%s', key %d would lead to a duplicate entry
253                            // 1761 Foreign key constraint for table '%s', record '%s' would lead to a duplicate entry in table '%s', key '%s'
254                            // 1762 Foreign key constraint for table '%s', record '%s' would lead to a duplicate entry in a child table
255                            1216 | 1217 | 1451 | 1452 | 1557 | 1761 | 1762 => {
256                                return Some(SqlErr::ForeignKeyConstraintViolation(
257                                    e.message().into(),
258                                ));
259                            }
260                            _ => return None,
261                        }
262                    }
263                    #[cfg(feature = "sqlx-postgres")]
264                    if e.try_downcast_ref::<sqlx::postgres::PgDatabaseError>()
265                        .is_some()
266                    {
267                        match _error_code_expanded {
268                            "23505" => {
269                                return Some(SqlErr::UniqueConstraintViolation(e.message().into()));
270                            }
271                            "23503" => {
272                                return Some(SqlErr::ForeignKeyConstraintViolation(
273                                    e.message().into(),
274                                ));
275                            }
276                            _ => return None,
277                        }
278                    }
279                    #[cfg(feature = "sqlx-sqlite")]
280                    if e.try_downcast_ref::<sqlx::sqlite::SqliteError>().is_some() {
281                        match _error_code_expanded {
282                            // error code 1555 refers to the primary key's unique constraint violation
283                            // error code 2067 refers to the UNIQUE unique constraint violation
284                            "1555" | "2067" => {
285                                return Some(SqlErr::UniqueConstraintViolation(e.message().into()));
286                            }
287                            "787" => {
288                                return Some(SqlErr::ForeignKeyConstraintViolation(
289                                    e.message().into(),
290                                ));
291                            }
292                            _ => return None,
293                        }
294                    }
295                }
296            }
297        }
298        #[cfg(feature = "rusqlite")]
299        if let DbErr::Exec(RuntimeErr::Rusqlite(err)) | DbErr::Query(RuntimeErr::Rusqlite(err)) =
300            self
301        {
302            use crate::driver::rusqlite::RusqliteError;
303            use std::ops::Deref;
304
305            if let RusqliteError::SqliteFailure(err, msg) = err.deref() {
306                match err.extended_code {
307                    1555 | 2067 => {
308                        return Some(SqlErr::UniqueConstraintViolation(
309                            msg.to_owned().unwrap_or_else(|| err.to_string()),
310                        ));
311                    }
312                    787 => {
313                        return Some(SqlErr::ForeignKeyConstraintViolation(
314                            msg.to_owned().unwrap_or_else(|| err.to_string()),
315                        ));
316                    }
317                    _ => (),
318                }
319            }
320        }
321        None
322    }
323}