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 + Send + Sync>,
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}
111
112/// An error from trying to get a row from a Model
113#[derive(Debug)]
114pub enum TryGetError {
115    /// A database error was encountered as defined in [crate::DbErr]
116    DbErr(DbErr),
117    /// A null value was encountered
118    Null(String),
119}
120
121/// Connection Acquire error
122#[derive(Error, Debug, PartialEq, Eq, Copy, Clone)]
123pub enum ConnAcquireErr {
124    /// Connection pool timed out
125    #[error("Connection pool timed out")]
126    Timeout,
127    /// Connection closed
128    #[error("Connection closed")]
129    ConnectionClosed,
130}
131
132/// Runtime error
133#[derive(Error, Debug, Clone)]
134pub enum RuntimeErr {
135    /// SQLx Error
136    #[cfg(feature = "sqlx-dep")]
137    #[error("{0}")]
138    SqlxError(Arc<sqlx::error::Error>),
139    /// Error generated from within SeaORM
140    #[error("{0}")]
141    Internal(String),
142}
143
144impl PartialEq for DbErr {
145    fn eq(&self, other: &Self) -> bool {
146        self.to_string() == other.to_string()
147    }
148}
149
150impl Eq for DbErr {}
151
152/// Error during `impl FromStr for Entity::Column`
153#[derive(Error, Debug)]
154#[error("Failed to match \"{0}\" as Column")]
155pub struct ColumnFromStrErr(pub String);
156
157#[allow(dead_code)]
158pub(crate) fn conn_err<T>(s: T) -> DbErr
159where
160    T: ToString,
161{
162    DbErr::Conn(RuntimeErr::Internal(s.to_string()))
163}
164
165#[allow(dead_code)]
166pub(crate) fn exec_err<T>(s: T) -> DbErr
167where
168    T: ToString,
169{
170    DbErr::Exec(RuntimeErr::Internal(s.to_string()))
171}
172
173#[allow(dead_code)]
174pub(crate) fn query_err<T>(s: T) -> DbErr
175where
176    T: ToString,
177{
178    DbErr::Query(RuntimeErr::Internal(s.to_string()))
179}
180
181#[allow(dead_code)]
182pub(crate) fn type_err<T>(s: T) -> DbErr
183where
184    T: ToString,
185{
186    DbErr::Type(s.to_string())
187}
188
189#[allow(dead_code)]
190pub(crate) fn json_err<T>(s: T) -> DbErr
191where
192    T: ToString,
193{
194    DbErr::Json(s.to_string())
195}
196
197/// An error from unsuccessful SQL query
198#[derive(Error, Debug, Clone, PartialEq, Eq)]
199#[non_exhaustive]
200pub enum SqlErr {
201    /// Error for duplicate record in unique field or primary key field
202    #[error("Unique Constraint Violated: {0}")]
203    UniqueConstraintViolation(String),
204    /// Error for Foreign key constraint
205    #[error("Foreign Key Constraint Violated: {0}")]
206    ForeignKeyConstraintViolation(String),
207}
208
209#[allow(dead_code)]
210impl DbErr {
211    /// Convert generic DbErr by sqlx to SqlErr, return none if the error is not any type of SqlErr
212    pub fn sql_err(&self) -> Option<SqlErr> {
213        #[cfg(any(
214            feature = "sqlx-mysql",
215            feature = "sqlx-postgres",
216            feature = "sqlx-sqlite"
217        ))]
218        {
219            use std::ops::Deref;
220            if let DbErr::Exec(RuntimeErr::SqlxError(e)) | DbErr::Query(RuntimeErr::SqlxError(e)) =
221                self
222            {
223                if let sqlx::Error::Database(e) = e.deref() {
224                    let error_code = e.code().unwrap_or_default();
225                    let _error_code_expanded = error_code.deref();
226                    #[cfg(feature = "sqlx-mysql")]
227                    if e.try_downcast_ref::<sqlx::mysql::MySqlDatabaseError>()
228                        .is_some()
229                    {
230                        let error_number = e
231                            .try_downcast_ref::<sqlx::mysql::MySqlDatabaseError>()?
232                            .number();
233                        match error_number {
234                            // 1022 Can't write; duplicate key in table '%s'
235                            // 1062 Duplicate entry '%s' for key %d
236                            // 1169 Can't write, because of unique constraint, to table '%s'
237                            // 1586 Duplicate entry '%s' for key '%s'
238                            1022 | 1062 | 1169 | 1586 => {
239                                return Some(SqlErr::UniqueConstraintViolation(e.message().into()));
240                            }
241                            // 1216 Cannot add or update a child row: a foreign key constraint fails
242                            // 1217 Cannot delete or update a parent row: a foreign key constraint fails
243                            // 1451 Cannot delete or update a parent row: a foreign key constraint fails (%s)
244                            // 1452 Cannot add or update a child row: a foreign key constraint fails (%s)
245                            // 1557 Upholding foreign key constraints for table '%s', entry '%s', key %d would lead to a duplicate entry
246                            // 1761 Foreign key constraint for table '%s', record '%s' would lead to a duplicate entry in table '%s', key '%s'
247                            // 1762 Foreign key constraint for table '%s', record '%s' would lead to a duplicate entry in a child table
248                            1216 | 1217 | 1451 | 1452 | 1557 | 1761 | 1762 => {
249                                return Some(SqlErr::ForeignKeyConstraintViolation(
250                                    e.message().into(),
251                                ));
252                            }
253                            _ => return None,
254                        }
255                    }
256                    #[cfg(feature = "sqlx-postgres")]
257                    if e.try_downcast_ref::<sqlx::postgres::PgDatabaseError>()
258                        .is_some()
259                    {
260                        match _error_code_expanded {
261                            "23505" => {
262                                return Some(SqlErr::UniqueConstraintViolation(e.message().into()));
263                            }
264                            "23503" => {
265                                return Some(SqlErr::ForeignKeyConstraintViolation(
266                                    e.message().into(),
267                                ));
268                            }
269                            _ => return None,
270                        }
271                    }
272                    #[cfg(feature = "sqlx-sqlite")]
273                    if e.try_downcast_ref::<sqlx::sqlite::SqliteError>().is_some() {
274                        match _error_code_expanded {
275                            // error code 1555 refers to the primary key's unique constraint violation
276                            // error code 2067 refers to the UNIQUE unique constraint violation
277                            "1555" | "2067" => {
278                                return Some(SqlErr::UniqueConstraintViolation(e.message().into()));
279                            }
280                            "787" => {
281                                return Some(SqlErr::ForeignKeyConstraintViolation(
282                                    e.message().into(),
283                                ));
284                            }
285                            _ => return None,
286                        }
287                    }
288                }
289            }
290        }
291        None
292    }
293}