sqlx_mysql/error.rs
1use std::error::Error as StdError;
2use std::fmt::{self, Debug, Display, Formatter};
3
4use crate::protocol::response::ErrPacket;
5
6use std::borrow::Cow;
7
8pub(crate) use sqlx_core::error::*;
9
10/// An error returned from the MySQL database.
11pub struct MySqlDatabaseError(pub(super) ErrPacket);
12
13impl MySqlDatabaseError {
14 /// The [SQLSTATE](https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html) code for this error.
15 pub fn code(&self) -> Option<&str> {
16 self.0.sql_state.as_deref()
17 }
18
19 /// The [number](https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html)
20 /// for this error.
21 ///
22 /// MySQL tends to use SQLSTATE as a general error category, and the error number as a more
23 /// granular indication of the error.
24 pub fn number(&self) -> u16 {
25 self.0.error_code
26 }
27
28 /// The human-readable error message.
29 pub fn message(&self) -> &str {
30 &self.0.error_message
31 }
32}
33
34impl Debug for MySqlDatabaseError {
35 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
36 f.debug_struct("MySqlDatabaseError")
37 .field("code", &self.code())
38 .field("number", &self.number())
39 .field("message", &self.message())
40 .finish()
41 }
42}
43
44impl Display for MySqlDatabaseError {
45 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
46 if let Some(code) = &self.code() {
47 write!(f, "{} ({}): {}", self.number(), code, self.message())
48 } else {
49 write!(f, "{}: {}", self.number(), self.message())
50 }
51 }
52}
53
54impl StdError for MySqlDatabaseError {}
55
56impl DatabaseError for MySqlDatabaseError {
57 #[inline]
58 fn message(&self) -> &str {
59 self.message()
60 }
61
62 #[inline]
63 fn code(&self) -> Option<Cow<'_, str>> {
64 self.code().map(Cow::Borrowed)
65 }
66
67 #[doc(hidden)]
68 fn as_error(&self) -> &(dyn StdError + Send + Sync + 'static) {
69 self
70 }
71
72 #[doc(hidden)]
73 fn as_error_mut(&mut self) -> &mut (dyn StdError + Send + Sync + 'static) {
74 self
75 }
76
77 #[doc(hidden)]
78 fn into_error(self: Box<Self>) -> Box<dyn StdError + Send + Sync + 'static> {
79 self
80 }
81
82 fn kind(&self) -> ErrorKind {
83 match self.number() {
84 error_codes::ER_DUP_KEY
85 | error_codes::ER_DUP_ENTRY
86 | error_codes::ER_DUP_UNIQUE
87 | error_codes::ER_DUP_ENTRY_WITH_KEY_NAME
88 | error_codes::ER_DUP_UNKNOWN_IN_INDEX => ErrorKind::UniqueViolation,
89
90 error_codes::ER_NO_REFERENCED_ROW
91 | error_codes::ER_NO_REFERENCED_ROW_2
92 | error_codes::ER_ROW_IS_REFERENCED
93 | error_codes::ER_ROW_IS_REFERENCED_2
94 | error_codes::ER_FK_COLUMN_NOT_NULL
95 | error_codes::ER_FK_CANNOT_DELETE_PARENT => ErrorKind::ForeignKeyViolation,
96
97 error_codes::ER_BAD_NULL_ERROR | error_codes::ER_NO_DEFAULT_FOR_FIELD => {
98 ErrorKind::NotNullViolation
99 }
100
101 error_codes::ER_CHECK_CONSTRAINT_VIOLATED => ErrorKind::CheckViolation,
102
103 // https://mariadb.com/kb/en/e4025/
104 error_codes::mariadb::ER_CONSTRAINT_FAILED
105 // MySQL uses this code for a completely different error,
106 // but we can differentiate by SQLSTATE:
107 // <https://dev.mysql.com/doc/mysql-errors/8.4/en/server-error-reference.html#error_er_innodb_autoextend_size_out_of_range
108 if self.0.sql_state.as_deref() == Some("23000") =>
109 {
110 ErrorKind::CheckViolation
111 }
112
113 _ => ErrorKind::Other,
114 }
115 }
116}
117
118/// The MySQL server uses SQLSTATEs as a generic error category,
119/// and returns a `error_code` instead within the error packet.
120///
121/// For reference: <https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html>.
122pub(crate) mod error_codes {
123 /// Caused when a DDL operation creates duplicated keys.
124 pub const ER_DUP_KEY: u16 = 1022;
125 /// Caused when a DML operation tries create a duplicated entry for a key,
126 /// be it a unique or primary one.
127 pub const ER_DUP_ENTRY: u16 = 1062;
128 /// Similar to `ER_DUP_ENTRY`, but only present in NDB clusters.
129 ///
130 /// See: <https://github.com/mysql/mysql-server/blob/fbdaa4def30d269bc4de5b85de61de34b11c0afc/mysql-test/suite/stress/include/ddl7.inc#L68>.
131 pub const ER_DUP_UNIQUE: u16 = 1169;
132 /// Similar to `ER_DUP_ENTRY`, but with a formatted string message.
133 ///
134 /// See: <https://bugs.mysql.com/bug.php?id=46976>.
135 pub const ER_DUP_ENTRY_WITH_KEY_NAME: u16 = 1586;
136 /// Caused when a DDL operation to add a unique index fails,
137 /// because duplicate items were created by concurrent DML operations.
138 /// When this happens, the key is unknown, so the server can't use `ER_DUP_KEY`.
139 ///
140 /// For example: an `INSERT` operation creates duplicate `name` fields when `ALTER`ing a table and making `name` unique.
141 pub const ER_DUP_UNKNOWN_IN_INDEX: u16 = 1859;
142
143 /// Caused when inserting an entry with a column with a value that does not reference a foreign row.
144 pub const ER_NO_REFERENCED_ROW: u16 = 1216;
145 /// Caused when deleting a row that is referenced in other tables.
146 pub const ER_ROW_IS_REFERENCED: u16 = 1217;
147 /// Caused when deleting a row that is referenced in other tables.
148 /// This differs from `ER_ROW_IS_REFERENCED` in that the error message contains the affected constraint.
149 pub const ER_ROW_IS_REFERENCED_2: u16 = 1451;
150 /// Caused when inserting an entry with a column with a value that does not reference a foreign row.
151 /// This differs from `ER_NO_REFERENCED_ROW` in that the error message contains the affected constraint.
152 pub const ER_NO_REFERENCED_ROW_2: u16 = 1452;
153 /// Caused when creating a FK with `ON DELETE SET NULL` or `ON UPDATE SET NULL` to a column that is `NOT NULL`, or vice-versa.
154 pub const ER_FK_COLUMN_NOT_NULL: u16 = 1830;
155 /// Removed in 5.7.3.
156 pub const ER_FK_CANNOT_DELETE_PARENT: u16 = 1834;
157
158 /// Caused when inserting a NULL value to a column marked as NOT NULL.
159 pub const ER_BAD_NULL_ERROR: u16 = 1048;
160 /// Caused when inserting a DEFAULT value to a column marked as NOT NULL, which also doesn't have a default value set.
161 pub const ER_NO_DEFAULT_FOR_FIELD: u16 = 1364;
162
163 /// Caused when a check constraint is violated.
164 ///
165 /// Only available after 8.0.16.
166 pub const ER_CHECK_CONSTRAINT_VIOLATED: u16 = 3819;
167
168 pub(crate) mod mariadb {
169 /// Error code emitted by MariaDB for constraint errors: <https://mariadb.com/kb/en/e4025/>
170 ///
171 /// MySQL emits this code for a completely different error:
172 /// <https://dev.mysql.com/doc/mysql-errors/8.4/en/server-error-reference.html#error_er_innodb_autoextend_size_out_of_range>
173 ///
174 /// You also check that SQLSTATE is `23000`.
175 pub const ER_CONSTRAINT_FAILED: u16 = 4025;
176 }
177}