1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
use std::error::Error as StdError;
use std::fmt::{self, Debug, Display, Formatter};

use crate::protocol::response::ErrPacket;

use std::borrow::Cow;

pub(crate) use sqlx_core::error::*;

/// An error returned from the MySQL database.
pub struct MySqlDatabaseError(pub(super) ErrPacket);

impl MySqlDatabaseError {
    /// The [SQLSTATE](https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html) code for this error.
    pub fn code(&self) -> Option<&str> {
        self.0.sql_state.as_deref()
    }

    /// The [number](https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html)
    /// for this error.
    ///
    /// MySQL tends to use SQLSTATE as a general error category, and the error number as a more
    /// granular indication of the error.
    pub fn number(&self) -> u16 {
        self.0.error_code
    }

    /// The human-readable error message.
    pub fn message(&self) -> &str {
        &self.0.error_message
    }
}

impl Debug for MySqlDatabaseError {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        f.debug_struct("MySqlDatabaseError")
            .field("code", &self.code())
            .field("number", &self.number())
            .field("message", &self.message())
            .finish()
    }
}

impl Display for MySqlDatabaseError {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        if let Some(code) = &self.code() {
            write!(f, "{} ({}): {}", self.number(), code, self.message())
        } else {
            write!(f, "{}: {}", self.number(), self.message())
        }
    }
}

impl StdError for MySqlDatabaseError {}

impl DatabaseError for MySqlDatabaseError {
    #[inline]
    fn message(&self) -> &str {
        self.message()
    }

    #[inline]
    fn code(&self) -> Option<Cow<'_, str>> {
        self.code().map(Cow::Borrowed)
    }

    #[doc(hidden)]
    fn as_error(&self) -> &(dyn StdError + Send + Sync + 'static) {
        self
    }

    #[doc(hidden)]
    fn as_error_mut(&mut self) -> &mut (dyn StdError + Send + Sync + 'static) {
        self
    }

    #[doc(hidden)]
    fn into_error(self: Box<Self>) -> Box<dyn StdError + Send + Sync + 'static> {
        self
    }

    fn kind(&self) -> ErrorKind {
        match self.number() {
            error_codes::ER_DUP_KEY
            | error_codes::ER_DUP_ENTRY
            | error_codes::ER_DUP_UNIQUE
            | error_codes::ER_DUP_ENTRY_WITH_KEY_NAME
            | error_codes::ER_DUP_UNKNOWN_IN_INDEX => ErrorKind::UniqueViolation,

            error_codes::ER_NO_REFERENCED_ROW
            | error_codes::ER_NO_REFERENCED_ROW_2
            | error_codes::ER_ROW_IS_REFERENCED
            | error_codes::ER_ROW_IS_REFERENCED_2
            | error_codes::ER_FK_COLUMN_NOT_NULL
            | error_codes::ER_FK_CANNOT_DELETE_PARENT => ErrorKind::ForeignKeyViolation,

            error_codes::ER_BAD_NULL_ERROR | error_codes::ER_NO_DEFAULT_FOR_FIELD => {
                ErrorKind::NotNullViolation
            }

            error_codes::ER_CHECK_CONSTRAINT_VIOLATED => ErrorKind::CheckViolation,

            _ => ErrorKind::Other,
        }
    }
}

/// The MySQL server uses SQLSTATEs as a generic error category,
/// and returns a `error_code` instead within the error packet.
///
/// For reference: <https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html>.
pub(crate) mod error_codes {
    /// Caused when a DDL operation creates duplicated keys.
    pub const ER_DUP_KEY: u16 = 1022;
    /// Caused when a DML operation tries create a duplicated entry for a key,
    /// be it a unique or primary one.
    pub const ER_DUP_ENTRY: u16 = 1062;
    /// Similar to `ER_DUP_ENTRY`, but only present in NDB clusters.
    ///
    /// See: <https://github.com/mysql/mysql-server/blob/fbdaa4def30d269bc4de5b85de61de34b11c0afc/mysql-test/suite/stress/include/ddl7.inc#L68>.
    pub const ER_DUP_UNIQUE: u16 = 1169;
    /// Similar to `ER_DUP_ENTRY`, but with a formatted string message.
    ///
    /// See: <https://bugs.mysql.com/bug.php?id=46976>.
    pub const ER_DUP_ENTRY_WITH_KEY_NAME: u16 = 1586;
    /// Caused when a DDL operation to add a unique index fails,
    /// because duplicate items were created by concurrent DML operations.
    /// When this happens, the key is unknown, so the server can't use `ER_DUP_KEY`.
    ///
    /// For example: an `INSERT` operation creates duplicate `name` fields when `ALTER`ing a table and making `name` unique.
    pub const ER_DUP_UNKNOWN_IN_INDEX: u16 = 1859;

    /// Caused when inserting an entry with a column with a value that does not reference a foreign row.
    pub const ER_NO_REFERENCED_ROW: u16 = 1216;
    /// Caused when deleting a row that is referenced in other tables.
    pub const ER_ROW_IS_REFERENCED: u16 = 1217;
    /// Caused when deleting a row that is referenced in other tables.
    /// This differs from `ER_ROW_IS_REFERENCED` in that the error message contains the affected constraint.
    pub const ER_ROW_IS_REFERENCED_2: u16 = 1451;
    /// Caused when inserting an entry with a column with a value that does not reference a foreign row.
    /// This differs from `ER_NO_REFERENCED_ROW` in that the error message contains the affected constraint.
    pub const ER_NO_REFERENCED_ROW_2: u16 = 1452;
    /// 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.
    pub const ER_FK_COLUMN_NOT_NULL: u16 = 1830;
    /// Removed in 5.7.3.
    pub const ER_FK_CANNOT_DELETE_PARENT: u16 = 1834;

    /// Caused when inserting a NULL value to a column marked as NOT NULL.
    pub const ER_BAD_NULL_ERROR: u16 = 1048;
    /// Caused when inserting a DEFAULT value to a column marked as NOT NULL, which also doesn't have a default value set.
    pub const ER_NO_DEFAULT_FOR_FIELD: u16 = 1364;

    /// Caused when a check constraint is violated.
    ///
    /// Only available after 8.0.16.
    pub const ER_CHECK_CONSTRAINT_VIOLATED: u16 = 3819;
}