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