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 thiserror::Error;
14
15/// An error from unsuccessful database operations
16#[derive(Error, Debug)]
17pub enum DbErr {
18    /// This error can happen when the connection pool is fully-utilized
19    #[error("Failed to acquire connection from pool: {0}")]
20    ConnectionAcquire(#[source] ConnAcquireErr),
21    /// Runtime type conversion error
22    #[error("Error converting `{from}` into `{into}`: {source}")]
23    TryIntoErr {
24        /// From type
25        from: &'static str,
26        /// Into type
27        into: &'static str,
28        /// TryError
29        source: Box<dyn std::error::Error + Send + Sync>,
30    },
31    /// There was a problem with the database connection
32    #[error("Connection Error: {0}")]
33    Conn(#[source] RuntimeErr),
34    /// An operation did not execute successfully
35    #[error("Execution Error: {0}")]
36    Exec(#[source] RuntimeErr),
37    /// An error occurred while performing a query
38    #[error("Query Error: {0}")]
39    Query(#[source] RuntimeErr),
40    /// Type error: the specified type cannot be converted from u64. This is not a runtime error.
41    #[error("Type '{0}' cannot be converted from u64")]
42    ConvertFromU64(&'static str),
43    /// After an insert statement it was impossible to retrieve the last_insert_id
44    #[error("Failed to unpack last_insert_id")]
45    UnpackInsertId,
46    /// When updating, a model should know its primary key to check
47    /// if the record has been correctly updated, otherwise this error will occur
48    #[error("Failed to get primary key from model")]
49    UpdateGetPrimaryKey,
50    /// The record was not found in the database
51    #[error("RecordNotFound Error: {0}")]
52    RecordNotFound(String),
53    /// Thrown by `TryFrom<ActiveModel>`, which assumes all attributes are set/unchanged
54    #[error("Attribute {0} is NotSet")]
55    AttrNotSet(String),
56    /// A custom error
57    #[error("Custom Error: {0}")]
58    Custom(String),
59    /// Error occurred while parsing value as target type
60    #[error("Type Error: {0}")]
61    Type(String),
62    /// Error occurred while parsing json value as target type
63    #[error("Json Error: {0}")]
64    Json(String),
65    /// A migration error
66    #[error("Migration Error: {0}")]
67    Migration(String),
68    /// None of the records are inserted,
69    /// that probably means all of them conflict with existing records in the table
70    #[error("None of the records are inserted")]
71    RecordNotInserted,
72    /// None of the records are updated, that means a WHERE condition has no matches.
73    /// May be the table is empty or the record does not exist
74    #[error("None of the records are updated")]
75    RecordNotUpdated,
76}
77
78/// An error from trying to get a row from a Model
79#[derive(Debug)]
80pub enum TryGetError {
81    /// A database error was encountered as defined in [crate::DbErr]
82    DbErr(DbErr),
83    /// A null value was encountered
84    Null(String),
85}
86
87/// Connection Acquire error
88#[derive(Error, Debug, PartialEq, Eq)]
89pub enum ConnAcquireErr {
90    /// Connection pool timed out
91    #[error("Connection pool timed out")]
92    Timeout,
93    /// Connection closed
94    #[error("Connection closed")]
95    ConnectionClosed,
96}
97
98/// Runtime error
99#[derive(Error, Debug)]
100pub enum RuntimeErr {
101    /// SQLx Error
102    #[cfg(feature = "sqlx-dep")]
103    #[error("{0}")]
104    SqlxError(#[source] sqlx::error::Error),
105    /// Error generated from within SeaORM
106    #[error("{0}")]
107    Internal(String),
108}
109
110impl PartialEq for DbErr {
111    fn eq(&self, other: &Self) -> bool {
112        self.to_string() == other.to_string()
113    }
114}
115
116impl Eq for DbErr {}
117
118/// Error during `impl FromStr for Entity::Column`
119#[derive(Error, Debug)]
120#[error("Failed to match \"{0}\" as Column")]
121pub struct ColumnFromStrErr(pub String);
122
123#[allow(dead_code)]
124pub(crate) fn conn_err<T>(s: T) -> DbErr
125where
126    T: ToString,
127{
128    DbErr::Conn(RuntimeErr::Internal(s.to_string()))
129}
130
131#[allow(dead_code)]
132pub(crate) fn exec_err<T>(s: T) -> DbErr
133where
134    T: ToString,
135{
136    DbErr::Exec(RuntimeErr::Internal(s.to_string()))
137}
138
139#[allow(dead_code)]
140pub(crate) fn query_err<T>(s: T) -> DbErr
141where
142    T: ToString,
143{
144    DbErr::Query(RuntimeErr::Internal(s.to_string()))
145}
146
147#[allow(dead_code)]
148pub(crate) fn type_err<T>(s: T) -> DbErr
149where
150    T: ToString,
151{
152    DbErr::Type(s.to_string())
153}
154
155#[allow(dead_code)]
156pub(crate) fn json_err<T>(s: T) -> DbErr
157where
158    T: ToString,
159{
160    DbErr::Json(s.to_string())
161}
162
163/// An error from unsuccessful SQL query
164#[derive(Error, Debug, Clone, PartialEq, Eq)]
165#[non_exhaustive]
166pub enum SqlErr {
167    /// Error for duplicate record in unique field or primary key field
168    #[error("Unique Constraint Violated: {0}")]
169    UniqueConstraintViolation(String),
170    /// Error for Foreign key constraint
171    #[error("Foreign Key Constraint Violated: {0}")]
172    ForeignKeyConstraintViolation(String),
173}
174
175#[allow(dead_code)]
176impl DbErr {
177    /// Convert generic DbErr by sqlx to SqlErr, return none if the error is not any type of SqlErr
178    pub fn sql_err(&self) -> Option<SqlErr> {
179        #[cfg(any(
180            feature = "sqlx-mysql",
181            feature = "sqlx-postgres",
182            feature = "sqlx-sqlite"
183        ))]
184        {
185            use std::ops::Deref;
186            if let DbErr::Exec(RuntimeErr::SqlxError(sqlx::Error::Database(e)))
187            | DbErr::Query(RuntimeErr::SqlxError(sqlx::Error::Database(e))) = self
188            {
189                let error_code = e.code().unwrap_or_default();
190                let _error_code_expanded = error_code.deref();
191                #[cfg(feature = "sqlx-mysql")]
192                if e.try_downcast_ref::<sqlx::mysql::MySqlDatabaseError>()
193                    .is_some()
194                {
195                    let error_number = e
196                        .try_downcast_ref::<sqlx::mysql::MySqlDatabaseError>()?
197                        .number();
198                    match error_number {
199                        // 1022 Can't write; duplicate key in table '%s'
200                        // 1062 Duplicate entry '%s' for key %d
201                        // 1169 Can't write, because of unique constraint, to table '%s'
202                        // 1586 Duplicate entry '%s' for key '%s'
203                        1022 | 1062 | 1169 | 1586 => {
204                            return Some(SqlErr::UniqueConstraintViolation(e.message().into()))
205                        }
206                        // 1216 Cannot add or update a child row: a foreign key constraint fails
207                        // 1217 Cannot delete or update a parent row: a foreign key constraint fails
208                        // 1451 Cannot delete or update a parent row: a foreign key constraint fails (%s)
209                        // 1452 Cannot add or update a child row: a foreign key constraint fails (%s)
210                        // 1557 Upholding foreign key constraints for table '%s', entry '%s', key %d would lead to a duplicate entry
211                        // 1761 Foreign key constraint for table '%s', record '%s' would lead to a duplicate entry in table '%s', key '%s'
212                        // 1762 Foreign key constraint for table '%s', record '%s' would lead to a duplicate entry in a child table
213                        1216 | 1217 | 1451 | 1452 | 1557 | 1761 | 1762 => {
214                            return Some(SqlErr::ForeignKeyConstraintViolation(e.message().into()))
215                        }
216                        _ => return None,
217                    }
218                }
219                #[cfg(feature = "sqlx-postgres")]
220                if e.try_downcast_ref::<sqlx::postgres::PgDatabaseError>()
221                    .is_some()
222                {
223                    match _error_code_expanded {
224                        "23505" => {
225                            return Some(SqlErr::UniqueConstraintViolation(e.message().into()))
226                        }
227                        "23503" => {
228                            return Some(SqlErr::ForeignKeyConstraintViolation(e.message().into()))
229                        }
230                        _ => return None,
231                    }
232                }
233                #[cfg(feature = "sqlx-sqlite")]
234                if e.try_downcast_ref::<sqlx::sqlite::SqliteError>().is_some() {
235                    match _error_code_expanded {
236                        // error code 1555 refers to the primary key's unique constraint violation
237                        // error code 2067 refers to the UNIQUE unique constraint violation
238                        "1555" | "2067" => {
239                            return Some(SqlErr::UniqueConstraintViolation(e.message().into()))
240                        }
241                        "787" => {
242                            return Some(SqlErr::ForeignKeyConstraintViolation(e.message().into()))
243                        }
244                        _ => return None,
245                    }
246                }
247            }
248        }
249        None
250    }
251}