1use super::{ffi, safe};
2use std::{fmt, cmp};
3use std::ffi::CStr;
4use std::error::Error;
5
6pub const MAX_DIAGNOSTIC_MESSAGE_SIZE: usize = 1024;
7
8pub struct DiagnosticRecord {
13 state: [ffi::SQLCHAR; ffi::SQL_SQLSTATE_SIZE + 1],
15 message: [ffi::SQLCHAR; MAX_DIAGNOSTIC_MESSAGE_SIZE],
17 message_length: ffi::SQLSMALLINT,
19 native_error: ffi::SQLINTEGER,
20 message_string: String,
21}
22
23impl DiagnosticRecord {
24 pub fn get_raw_state(&self) -> &[u8] {
26 &self.state
27 }
28 pub fn get_raw_message(&self) -> &[u8] {
30 &self.message[0..self.message_length as usize]
31 }
32 pub fn get_native_error(&self) -> i32 {
34 self.native_error
35 }
36 pub fn empty() -> DiagnosticRecord {
39 let message = b"No SQL-driver error information available.";
40 let mut rec = DiagnosticRecord {
41 state: b"HY000\0".clone(),
42 message: [0u8; MAX_DIAGNOSTIC_MESSAGE_SIZE],
43 native_error: -1,
44 message_length: message.len() as ffi::SQLSMALLINT,
45 message_string: String::from(""),
46 };
47 rec.message[..message.len()].copy_from_slice(message);
48 rec
49 }
50}
51
52impl fmt::Display for DiagnosticRecord {
53 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
54 let state = CStr::from_bytes_with_nul(&self.state).unwrap();
56
57 write!(
58 f,
59 "State: {}, Native error: {}, Message: {}",
60 state.to_str().unwrap(),
61 self.native_error,
62 self.message_string,
63 )
64 }
65}
66
67impl fmt::Debug for DiagnosticRecord {
68 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69 fmt::Display::fmt(self, f)
70 }
71}
72
73impl Error for DiagnosticRecord {
74 fn description(&self) -> &str {
75 &self.message_string
76 }
77 fn cause(&self) -> Option<& dyn Error> {
78 None
79 }
80}
81
82pub trait GetDiagRec {
85 fn get_diag_rec(&self, record_number: i16) -> Option<DiagnosticRecord>;
90}
91
92impl<D> GetDiagRec for D
93where
94 D: safe::Diagnostics,
95{
96 fn get_diag_rec(&self, record_number: i16) -> Option<(DiagnosticRecord)> {
97 use safe::ReturnOption::*;
98 let mut message = [0; MAX_DIAGNOSTIC_MESSAGE_SIZE];
99 match self.diagnostics(record_number, &mut message) {
100 Success(result) | Info(result) => {
101 let mut message_length = cmp::min(result.text_length, MAX_DIAGNOSTIC_MESSAGE_SIZE as ffi::SQLSMALLINT - 1);
103 while message_length > 0 && message[(message_length - 1) as usize] == 0 {
105 message_length = message_length - 1;
106 }
107 Some(DiagnosticRecord {
108 state: result.state,
109 native_error: result.native_error,
110 message_length,
111 message,
112 message_string: unsafe {
113 ::environment::OS_ENCODING.decode(&message[0..message_length as usize]).0.into_owned()
114 },
115 })
116 }
117 NoData(()) => None,
118 Error(()) => panic!("Diagnostics returned error for record number {}. Record numbers have to be at least 1.", record_number),
119 }
120 }
121}
122
123#[cfg(test)]
124mod test {
125
126 use super::*;
127
128 impl DiagnosticRecord {
129 fn new() -> DiagnosticRecord {
130 DiagnosticRecord {
131 state: [0u8; ffi::SQL_SQLSTATE_SIZE + 1],
132 message: [0u8; MAX_DIAGNOSTIC_MESSAGE_SIZE],
133 native_error: 0,
134 message_length: 0,
135 message_string: String::from(""),
136 }
137 }
138 }
139
140 #[test]
141 fn formatting() {
142
143 let message = b"[Microsoft][ODBC Driver Manager] Function sequence error\0";
145 let mut rec = DiagnosticRecord::new();
146 rec.state = b"HY010\0".clone();
147 rec.message_string = CStr::from_bytes_with_nul(message).unwrap().to_str().unwrap().to_owned();
148 rec.message_length = 56;
149 for i in 0..(rec.message_length as usize) {
150 rec.message[i] = message[i];
151 }
152
153 assert_eq!(
155 format!("{}", rec),
156 "State: HY010, Native error: 0, Message: [Microsoft][ODBC Driver Manager] \
157 Function sequence error"
158 );
159 }
160}