sqlx_core_oldapi/odbc/
error.rs1use crate::error::DatabaseError;
2use odbc_api::{
3 handles::{slice_to_cow_utf8, Record},
4 Error as OdbcApiError,
5};
6use std::borrow::Cow;
7use std::fmt::{Display, Formatter, Result as FmtResult};
8
9#[derive(Debug)]
10pub struct OdbcDatabaseError {
11 error: OdbcApiError,
12 message: String,
13 code: Option<String>,
14}
15
16impl OdbcDatabaseError {
17 fn diagnostic_record(error: &OdbcApiError) -> Option<&Record> {
18 match error {
19 OdbcApiError::Diagnostics { record, .. } => Some(record),
20 OdbcApiError::InvalidRowArraySize { record, .. } => Some(record),
21 OdbcApiError::UnsupportedOdbcApiVersion(record) => Some(record),
22 OdbcApiError::UnableToRepresentNull(record) => Some(record),
23 OdbcApiError::OracleOdbcDriverDoesNotSupport64Bit(record) => Some(record),
24 _ => None,
25 }
26 }
27
28 fn diagnostic_code(record: &Record) -> Option<String> {
29 let code = record.state.as_str();
30
31 if code.as_bytes().iter().all(|&byte| byte == 0) {
32 None
33 } else {
34 Some(code.to_owned())
35 }
36 }
37}
38
39impl From<OdbcApiError> for OdbcDatabaseError {
40 fn from(error: OdbcApiError) -> Self {
41 let record = Self::diagnostic_record(&error);
42 let message = record
43 .map(|record| slice_to_cow_utf8(&record.message).into_owned())
44 .filter(|message| !message.is_empty())
45 .unwrap_or_else(|| error.to_string());
46 let code = record.and_then(Self::diagnostic_code);
47
48 Self {
49 error,
50 message,
51 code,
52 }
53 }
54}
55
56impl Display for OdbcDatabaseError {
57 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
58 Display::fmt(&self.error, f)
59 }
60}
61
62impl std::error::Error for OdbcDatabaseError {}
63
64impl DatabaseError for OdbcDatabaseError {
65 fn message(&self) -> &str {
66 &self.message
67 }
68 fn code(&self) -> Option<Cow<'_, str>> {
69 self.code.as_deref().map(Cow::Borrowed)
70 }
71 fn as_error(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
72 self
73 }
74 fn as_error_mut(&mut self) -> &mut (dyn std::error::Error + Send + Sync + 'static) {
75 self
76 }
77 fn into_error(self: Box<Self>) -> Box<dyn std::error::Error + Send + Sync + 'static> {
78 self
79 }
80}
81
82impl From<OdbcApiError> for crate::error::Error {
83 fn from(value: OdbcApiError) -> Self {
84 crate::error::Error::Database(Box::new(OdbcDatabaseError::from(value)))
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91 use crate::error::DatabaseError;
92 use odbc_api::handles::{Record, SqlChar, State};
93
94 fn sql_chars(text: &str) -> Vec<SqlChar> {
95 text.bytes().map(Into::into).collect()
96 }
97
98 #[test]
99 fn database_error_uses_odbc_diagnostics_for_message_and_code() {
100 let error = OdbcDatabaseError::from(OdbcApiError::Diagnostics {
101 function: "SQLExecDirect",
102 record: Record {
103 state: State(*b"HY000"),
104 native_error: 1234,
105 message: sql_chars("syntax error near FROM"),
106 },
107 });
108
109 assert_eq!(error.message(), "syntax error near FROM");
110 assert_eq!(error.code().as_deref(), Some("HY000"));
111 }
112}