Skip to main content

diesel/result/
sql_state.rs

1use core::error::Error as StdError;
2use core::fmt;
3use core::str::FromStr;
4
5/// A [SQLSTATE](https://en.wikipedia.org/wiki/SQLSTATE) error code, including
6/// custom codes outside the standard, usable as a `match` pattern.
7///
8/// ```rust
9/// use diesel::result::{SqlState, SqlStateClass};
10///
11/// const CUSTOM: SqlState = SqlState::new(*b"P1001").unwrap();
12///
13/// let code: SqlState = "P1001".parse().unwrap();
14/// assert!(matches!(code, CUSTOM));
15/// assert_eq!(code.as_str(), "P1001");
16/// assert_eq!(code.class().as_str(), "P1");
17/// assert!(!code.class().is_standard());
18/// assert_eq!(code.subclass(), "001");
19///
20/// let code: SqlState = "23505".parse().unwrap();
21/// assert_eq!(code.class(), SqlStateClass::INTEGRITY_CONSTRAINT_VIOLATION);
22/// ```
23#[derive(#[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for SqlState {
}
#[automatically_derived]impl ::core::clone::Clone for SqlState {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<[u8; 5]>;
        *self
    }
}Clone, #[automatically_derived]impl ::core::marker::Copy for SqlState { }Copy, #[automatically_derived]impl ::core::marker::StructuralPartialEq for SqlState
    {
}
#[automatically_derived]impl ::core::cmp::PartialEq for SqlState {
    #[inline]
    fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]impl ::core::cmp::Eq for SqlState {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<[u8; 5]>;
    }
}Eq, #[automatically_derived]impl ::core::hash::Hash for SqlState {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        ::core::hash::Hash::hash(&self.0, state)
    }
}Hash, #[automatically_derived]impl ::core::cmp::PartialOrd for SqlState {
    #[inline]
    fn partial_cmp(&self, other: &Self)
        -> ::core::option::Option<::core::cmp::Ordering> {
        ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other))
    }
}PartialOrd, #[automatically_derived]impl ::core::cmp::Ord for SqlState {
    #[inline]
    fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
        ::core::cmp::Ord::cmp(&self.0, &other.0)
    }
}Ord)]
24pub struct SqlState([u8; 5]);
25
26impl SqlState {
27    /// Returns `None` unless every byte is an ASCII digit or uppercase letter.
28    pub const fn new(code: [u8; 5]) -> Option<Self> {
29        let mut i = 0;
30        while i < code.len() {
31            if !#[allow(non_exhaustive_omitted_patterns)] match code[i] {
    b'0'..=b'9' | b'A'..=b'Z' => true,
    _ => false,
}matches!(code[i], b'0'..=b'9' | b'A'..=b'Z') {
32                return None;
33            }
34            i += 1;
35        }
36        Some(Self(code))
37    }
38
39    /// The five character code.
40    pub fn as_str(&self) -> &str {
41        core::str::from_utf8(&self.0).expect("a SQLSTATE consists of ASCII bytes")
42    }
43
44    /// The [`SqlStateClass`], made of the first two characters.
45    pub fn class(&self) -> SqlStateClass {
46        SqlStateClass([self.0[0], self.0[1]])
47    }
48
49    /// The subclass, made of the last three characters, where `000` means none.
50    pub fn subclass(&self) -> &str {
51        &self.as_str()[2..]
52    }
53}
54
55impl fmt::Debug for SqlState {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        f.debug_tuple("SqlState").field(&self.as_str()).finish()
58    }
59}
60
61impl fmt::Display for SqlState {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        f.write_str(self.as_str())
64    }
65}
66
67impl FromStr for SqlState {
68    type Err = InvalidSqlState;
69
70    fn from_str(s: &str) -> Result<Self, Self::Err> {
71        <[u8; 5]>::try_from(s.as_bytes())
72            .ok()
73            .and_then(Self::new)
74            .ok_or(InvalidSqlState)
75    }
76}
77
78/// The error parsing a [`SqlState`] from a string that is not five characters
79/// from `0`-`9` and `A`-`Z`.
80#[derive(#[automatically_derived]impl ::core::fmt::Debug for InvalidSqlState {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "InvalidSqlState")
    }
}Debug, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for InvalidSqlState {
}
#[automatically_derived]impl ::core::clone::Clone for InvalidSqlState {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]impl ::core::marker::Copy for InvalidSqlState { }Copy, #[automatically_derived]impl ::core::marker::StructuralPartialEq for
    InvalidSqlState {
}
#[automatically_derived]impl ::core::cmp::PartialEq for InvalidSqlState {
    #[inline]
    fn eq(&self, other: &Self) -> bool { true }
}PartialEq, #[automatically_derived]impl ::core::cmp::Eq for InvalidSqlState { }Eq)]
81pub struct InvalidSqlState;
82
83impl fmt::Display for InvalidSqlState {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        f.write_str("a SQLSTATE consists of five characters from `0`-`9` and `A`-`Z`")
86    }
87}
88
89impl StdError for InvalidSqlState {}
90
91/// The class of a [`SqlState`], with a constant for each class
92/// [SQL:2011](https://en.wikipedia.org/wiki/SQLSTATE) defines.
93#[derive(#[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for SqlStateClass {
}
#[automatically_derived]impl ::core::clone::Clone for SqlStateClass {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<[u8; 2]>;
        *self
    }
}Clone, #[automatically_derived]impl ::core::marker::Copy for SqlStateClass { }Copy, #[automatically_derived]impl ::core::marker::StructuralPartialEq for
    SqlStateClass {
}
#[automatically_derived]impl ::core::cmp::PartialEq for SqlStateClass {
    #[inline]
    fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]impl ::core::cmp::Eq for SqlStateClass {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<[u8; 2]>;
    }
}Eq, #[automatically_derived]impl ::core::hash::Hash for SqlStateClass {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        ::core::hash::Hash::hash(&self.0, state)
    }
}Hash, #[automatically_derived]impl ::core::cmp::PartialOrd for SqlStateClass {
    #[inline]
    fn partial_cmp(&self, other: &Self)
        -> ::core::option::Option<::core::cmp::Ordering> {
        ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other))
    }
}PartialOrd, #[automatically_derived]impl ::core::cmp::Ord for SqlStateClass {
    #[inline]
    fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
        ::core::cmp::Ord::cmp(&self.0, &other.0)
    }
}Ord)]
94pub struct SqlStateClass([u8; 2]);
95
96impl SqlStateClass {
97    /// The two character code of the class.
98    pub fn as_str(&self) -> &str {
99        core::str::from_utf8(&self.0).expect("a SQLSTATE consists of ASCII bytes")
100    }
101
102    /// Whether the class starts with `0`-`4` or `A`-`H`, which the SQL standard reserves.
103    pub fn is_standard(&self) -> bool {
104        #[allow(non_exhaustive_omitted_patterns)] match self.0[0] {
    b'0'..=b'4' | b'A'..=b'H' => true,
    _ => false,
}matches!(self.0[0], b'0'..=b'4' | b'A'..=b'H')
105    }
106}
107
108impl fmt::Debug for SqlStateClass {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        f.debug_tuple("SqlStateClass")
111            .field(&self.as_str())
112            .finish()
113    }
114}
115
116impl fmt::Display for SqlStateClass {
117    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118        f.write_str(self.as_str())
119    }
120}
121
122macro_rules! sql_state_classes {
123    ($($name:ident = $code:literal, $condition:literal;)*) => {
124        impl SqlStateClass {
125            $(
126                #[doc = concat!("`", $code, "`, ", $condition, ".")]
127                pub const $name: Self = {
128                    let code = $code.as_bytes();
129                    Self([code[0], code[1]])
130                };
131            )*
132        }
133    };
134}
135
136impl SqlStateClass {
    #[doc = "`00`, successful completion."]
    pub const SUCCESSFUL_COMPLETION: Self =
        { let code = "00".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`01`, warning."]
    pub const WARNING: Self =
        { let code = "01".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`02`, no data."]
    pub const NO_DATA: Self =
        { let code = "02".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`07`, dynamic SQL error."]
    pub const DYNAMIC_SQL_ERROR: Self =
        { let code = "07".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`08`, connection exception."]
    pub const CONNECTION_EXCEPTION: Self =
        { let code = "08".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`09`, triggered action exception."]
    pub const TRIGGERED_ACTION_EXCEPTION: Self =
        { let code = "09".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`0A`, feature not supported."]
    pub const FEATURE_NOT_SUPPORTED: Self =
        { let code = "0A".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`0D`, invalid target type specification."]
    pub const INVALID_TARGET_TYPE_SPECIFICATION: Self =
        { let code = "0D".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`0E`, invalid schema name list specification."]
    pub const INVALID_SCHEMA_NAME_LIST_SPECIFICATION: Self =
        { let code = "0E".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`0F`, locator exception."]
    pub const LOCATOR_EXCEPTION: Self =
        { let code = "0F".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`0K`, resignal when handler not active."]
    pub const RESIGNAL_WHEN_HANDLER_NOT_ACTIVE: Self =
        { let code = "0K".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`0L`, invalid grantor."]
    pub const INVALID_GRANTOR: Self =
        { let code = "0L".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`0M`, invalid SQL-invoked procedure reference."]
    pub const INVALID_SQL_INVOKED_PROCEDURE_REFERENCE: Self =
        { let code = "0M".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`0N`, SQL/XML mapping error."]
    pub const SQL_XML_MAPPING_ERROR: Self =
        { let code = "0N".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`0P`, invalid role specification."]
    pub const INVALID_ROLE_SPECIFICATION: Self =
        { let code = "0P".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`0S`, invalid transform group name specification."]
    pub const INVALID_TRANSFORM_GROUP_NAME_SPECIFICATION: Self =
        { let code = "0S".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`0T`, target table disagrees with cursor specification."]
    pub const TARGET_TABLE_DISAGREES_WITH_CURSOR_SPECIFICATION: Self =
        { let code = "0T".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`0U`, attempt to assign to non-updatable column."]
    pub const ATTEMPT_TO_ASSIGN_TO_NON_UPDATABLE_COLUMN: Self =
        { let code = "0U".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`0V`, attempt to assign to ordering column."]
    pub const ATTEMPT_TO_ASSIGN_TO_ORDERING_COLUMN: Self =
        { let code = "0V".as_bytes(); Self([code[0], code[1]]) };
    #[doc =
    "`0W`, prohibited statement encountered during trigger execution."]
    pub const PROHIBITED_STATEMENT_ENCOUNTERED_DURING_TRIGGER_EXECUTION: Self
        =
        { let code = "0W".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`0X`, invalid foreign server specification."]
    pub const INVALID_FOREIGN_SERVER_SPECIFICATION: Self =
        { let code = "0X".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`0Y`, pass-through specific condition."]
    pub const PASS_THROUGH_SPECIFIC_CONDITION: Self =
        { let code = "0Y".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`0Z`, diagnostics exception."]
    pub const DIAGNOSTICS_EXCEPTION: Self =
        { let code = "0Z".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`10`, XQuery error."]
    pub const XQUERY_ERROR: Self =
        { let code = "10".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`20`, case not found for case statement."]
    pub const CASE_NOT_FOUND_FOR_CASE_STATEMENT: Self =
        { let code = "20".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`21`, cardinality violation."]
    pub const CARDINALITY_VIOLATION: Self =
        { let code = "21".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`22`, data exception."]
    pub const DATA_EXCEPTION: Self =
        { let code = "22".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`23`, integrity constraint violation."]
    pub const INTEGRITY_CONSTRAINT_VIOLATION: Self =
        { let code = "23".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`24`, invalid cursor state."]
    pub const INVALID_CURSOR_STATE: Self =
        { let code = "24".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`25`, invalid transaction state."]
    pub const INVALID_TRANSACTION_STATE: Self =
        { let code = "25".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`26`, invalid SQL statement name."]
    pub const INVALID_SQL_STATEMENT_NAME: Self =
        { let code = "26".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`27`, triggered data change violation."]
    pub const TRIGGERED_DATA_CHANGE_VIOLATION: Self =
        { let code = "27".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`28`, invalid authorization specification."]
    pub const INVALID_AUTHORIZATION_SPECIFICATION: Self =
        { let code = "28".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`2B`, dependent privilege descriptors still exist."]
    pub const DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST: Self =
        { let code = "2B".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`2C`, invalid character set name."]
    pub const INVALID_CHARACTER_SET_NAME: Self =
        { let code = "2C".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`2D`, invalid transaction termination."]
    pub const INVALID_TRANSACTION_TERMINATION: Self =
        { let code = "2D".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`2E`, invalid connection name."]
    pub const INVALID_CONNECTION_NAME: Self =
        { let code = "2E".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`2F`, SQL routine exception."]
    pub const SQL_ROUTINE_EXCEPTION: Self =
        { let code = "2F".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`2H`, invalid collation name."]
    pub const INVALID_COLLATION_NAME: Self =
        { let code = "2H".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`30`, invalid SQL statement identifier."]
    pub const INVALID_SQL_STATEMENT_IDENTIFIER: Self =
        { let code = "30".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`33`, invalid SQL descriptor name."]
    pub const INVALID_SQL_DESCRIPTOR_NAME: Self =
        { let code = "33".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`34`, invalid cursor name."]
    pub const INVALID_CURSOR_NAME: Self =
        { let code = "34".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`35`, invalid condition number."]
    pub const INVALID_CONDITION_NUMBER: Self =
        { let code = "35".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`36`, cursor sensitivity exception."]
    pub const CURSOR_SENSITIVITY_EXCEPTION: Self =
        { let code = "36".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`38`, external routine exception."]
    pub const EXTERNAL_ROUTINE_EXCEPTION: Self =
        { let code = "38".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`39`, external routine invocation exception."]
    pub const EXTERNAL_ROUTINE_INVOCATION_EXCEPTION: Self =
        { let code = "39".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`3B`, savepoint exception."]
    pub const SAVEPOINT_EXCEPTION: Self =
        { let code = "3B".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`3C`, ambiguous cursor name."]
    pub const AMBIGUOUS_CURSOR_NAME: Self =
        { let code = "3C".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`3D`, invalid catalog name."]
    pub const INVALID_CATALOG_NAME: Self =
        { let code = "3D".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`3F`, invalid schema name."]
    pub const INVALID_SCHEMA_NAME: Self =
        { let code = "3F".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`40`, transaction rollback."]
    pub const TRANSACTION_ROLLBACK: Self =
        { let code = "40".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`42`, syntax error or access rule violation."]
    pub const SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION: Self =
        { let code = "42".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`44`, with check option violation."]
    pub const WITH_CHECK_OPTION_VIOLATION: Self =
        { let code = "44".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`45`, unhandled user-defined exception."]
    pub const UNHANDLED_USER_DEFINED_EXCEPTION: Self =
        { let code = "45".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`46`, OLB-specific error."]
    pub const OLB_SPECIFIC_ERROR: Self =
        { let code = "46".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`HV`, FDW-specific condition."]
    pub const FDW_SPECIFIC_CONDITION: Self =
        { let code = "HV".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`HW`, datalink exception."]
    pub const DATALINK_EXCEPTION: Self =
        { let code = "HW".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`HY`, CLI-specific condition."]
    pub const CLI_SPECIFIC_CONDITION: Self =
        { let code = "HY".as_bytes(); Self([code[0], code[1]]) };
    #[doc = "`HZ`, remote database access."]
    pub const REMOTE_DATABASE_ACCESS: Self =
        { let code = "HZ".as_bytes(); Self([code[0], code[1]]) };
}sql_state_classes! {
137    SUCCESSFUL_COMPLETION = "00", "successful completion";
138    WARNING = "01", "warning";
139    NO_DATA = "02", "no data";
140    DYNAMIC_SQL_ERROR = "07", "dynamic SQL error";
141    CONNECTION_EXCEPTION = "08", "connection exception";
142    TRIGGERED_ACTION_EXCEPTION = "09", "triggered action exception";
143    FEATURE_NOT_SUPPORTED = "0A", "feature not supported";
144    INVALID_TARGET_TYPE_SPECIFICATION = "0D", "invalid target type specification";
145    INVALID_SCHEMA_NAME_LIST_SPECIFICATION = "0E", "invalid schema name list specification";
146    LOCATOR_EXCEPTION = "0F", "locator exception";
147    RESIGNAL_WHEN_HANDLER_NOT_ACTIVE = "0K", "resignal when handler not active";
148    INVALID_GRANTOR = "0L", "invalid grantor";
149    INVALID_SQL_INVOKED_PROCEDURE_REFERENCE = "0M", "invalid SQL-invoked procedure reference";
150    SQL_XML_MAPPING_ERROR = "0N", "SQL/XML mapping error";
151    INVALID_ROLE_SPECIFICATION = "0P", "invalid role specification";
152    INVALID_TRANSFORM_GROUP_NAME_SPECIFICATION = "0S", "invalid transform group name specification";
153    TARGET_TABLE_DISAGREES_WITH_CURSOR_SPECIFICATION = "0T", "target table disagrees with cursor specification";
154    ATTEMPT_TO_ASSIGN_TO_NON_UPDATABLE_COLUMN = "0U", "attempt to assign to non-updatable column";
155    ATTEMPT_TO_ASSIGN_TO_ORDERING_COLUMN = "0V", "attempt to assign to ordering column";
156    PROHIBITED_STATEMENT_ENCOUNTERED_DURING_TRIGGER_EXECUTION = "0W", "prohibited statement encountered during trigger execution";
157    INVALID_FOREIGN_SERVER_SPECIFICATION = "0X", "invalid foreign server specification";
158    PASS_THROUGH_SPECIFIC_CONDITION = "0Y", "pass-through specific condition";
159    DIAGNOSTICS_EXCEPTION = "0Z", "diagnostics exception";
160    XQUERY_ERROR = "10", "XQuery error";
161    CASE_NOT_FOUND_FOR_CASE_STATEMENT = "20", "case not found for case statement";
162    CARDINALITY_VIOLATION = "21", "cardinality violation";
163    DATA_EXCEPTION = "22", "data exception";
164    INTEGRITY_CONSTRAINT_VIOLATION = "23", "integrity constraint violation";
165    INVALID_CURSOR_STATE = "24", "invalid cursor state";
166    INVALID_TRANSACTION_STATE = "25", "invalid transaction state";
167    INVALID_SQL_STATEMENT_NAME = "26", "invalid SQL statement name";
168    TRIGGERED_DATA_CHANGE_VIOLATION = "27", "triggered data change violation";
169    INVALID_AUTHORIZATION_SPECIFICATION = "28", "invalid authorization specification";
170    DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST = "2B", "dependent privilege descriptors still exist";
171    INVALID_CHARACTER_SET_NAME = "2C", "invalid character set name";
172    INVALID_TRANSACTION_TERMINATION = "2D", "invalid transaction termination";
173    INVALID_CONNECTION_NAME = "2E", "invalid connection name";
174    SQL_ROUTINE_EXCEPTION = "2F", "SQL routine exception";
175    INVALID_COLLATION_NAME = "2H", "invalid collation name";
176    INVALID_SQL_STATEMENT_IDENTIFIER = "30", "invalid SQL statement identifier";
177    INVALID_SQL_DESCRIPTOR_NAME = "33", "invalid SQL descriptor name";
178    INVALID_CURSOR_NAME = "34", "invalid cursor name";
179    INVALID_CONDITION_NUMBER = "35", "invalid condition number";
180    CURSOR_SENSITIVITY_EXCEPTION = "36", "cursor sensitivity exception";
181    EXTERNAL_ROUTINE_EXCEPTION = "38", "external routine exception";
182    EXTERNAL_ROUTINE_INVOCATION_EXCEPTION = "39", "external routine invocation exception";
183    SAVEPOINT_EXCEPTION = "3B", "savepoint exception";
184    AMBIGUOUS_CURSOR_NAME = "3C", "ambiguous cursor name";
185    INVALID_CATALOG_NAME = "3D", "invalid catalog name";
186    INVALID_SCHEMA_NAME = "3F", "invalid schema name";
187    TRANSACTION_ROLLBACK = "40", "transaction rollback";
188    SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION = "42", "syntax error or access rule violation";
189    WITH_CHECK_OPTION_VIOLATION = "44", "with check option violation";
190    UNHANDLED_USER_DEFINED_EXCEPTION = "45", "unhandled user-defined exception";
191    OLB_SPECIFIC_ERROR = "46", "OLB-specific error";
192    FDW_SPECIFIC_CONDITION = "HV", "FDW-specific condition";
193    DATALINK_EXCEPTION = "HW", "datalink exception";
194    CLI_SPECIFIC_CONDITION = "HY", "CLI-specific condition";
195    REMOTE_DATABASE_ACCESS = "HZ", "remote database access";
196}
197
198#[cfg(test)]
199mod tests {
200    use super::*;
201
202    #[test]
203    fn new_accepts_only_digits_and_uppercase_letters() {
204        assert!(SqlState::new(*b"09AZ0").is_some());
205        for invalid in [b'/', b':', b'@', b'[', b'a', b'z', b' ', 0, 0xC3] {
206            let mut code = *b"23505";
207            code[4] = invalid;
208            assert_eq!(SqlState::new(code), None, "byte {invalid:#04x}");
209        }
210    }
211
212    #[test]
213    fn from_str_requires_exactly_five_bytes() {
214        assert_eq!("23505".parse::<SqlState>().unwrap().as_str(), "23505");
215        for invalid in ["", "2350", "235050", "2350\u{e9}", "2350\u{0}", "23505 "] {
216            assert_eq!(
217                invalid.parse::<SqlState>(),
218                Err(InvalidSqlState),
219                "{invalid:?}"
220            );
221        }
222    }
223
224    #[test]
225    fn class_and_subclass_split_after_two_characters() {
226        let code: SqlState = "40P01".parse().unwrap();
227        assert_eq!(code.class(), SqlStateClass::TRANSACTION_ROLLBACK);
228        assert_eq!(code.class().as_str(), "40");
229        assert_eq!(code.subclass(), "P01");
230    }
231
232    #[test]
233    fn standard_classes_start_with_0_to_4_or_a_to_h() {
234        for code in [
235            "00000", "40001", "0A000", "B0000", "G0000", "HV000", "H0000",
236        ] {
237            let code: SqlState = code.parse().unwrap();
238            assert!(code.class().is_standard(), "{code}");
239        }
240        for code in ["50000", "9Z000", "I0000", "P0001", "XX000"] {
241            let code: SqlState = code.parse().unwrap();
242            assert!(!code.class().is_standard(), "{code}");
243        }
244    }
245
246    #[test]
247    fn constants_match_as_patterns() {
248        const UNIQUE_VIOLATION: SqlState = SqlState::new(*b"23505").unwrap();
249        let code: SqlState = "23505".parse().unwrap();
250        assert!(matches!(code, UNIQUE_VIOLATION));
251        assert!(matches!(
252            code.class(),
253            SqlStateClass::INTEGRITY_CONSTRAINT_VIOLATION
254        ));
255        let other: SqlState = "22012".parse().unwrap();
256        assert!(!matches!(other, UNIQUE_VIOLATION));
257        assert!(!matches!(
258            other.class(),
259            SqlStateClass::INTEGRITY_CONSTRAINT_VIOLATION
260        ));
261    }
262}