use super::{ffi, safe};
use std::{fmt, cmp};
use std::ffi::CStr;
use std::error::Error;
pub const MAX_DIAGNOSTIC_MESSAGE_SIZE: usize = 1024;
pub struct DiagnosticRecord {
state: [ffi::SQLCHAR; ffi::SQL_SQLSTATE_SIZE + 1],
message: [ffi::SQLCHAR; MAX_DIAGNOSTIC_MESSAGE_SIZE],
message_length: ffi::SQLSMALLINT,
native_error: ffi::SQLINTEGER,
message_string: String,
}
impl DiagnosticRecord {
pub fn get_raw_state(&self) -> &[u8] {
&self.state
}
pub fn get_raw_message(&self) -> &[u8] {
&self.message[0..self.message_length as usize]
}
pub fn get_native_error(&self) -> i32 {
self.native_error
}
pub fn empty() -> DiagnosticRecord {
let message = b"No SQL-driver error information available.";
let mut rec = DiagnosticRecord {
state: b"HY000\0".clone(),
message: [0u8; MAX_DIAGNOSTIC_MESSAGE_SIZE],
native_error: -1,
message_length: message.len() as ffi::SQLSMALLINT,
message_string: String::from(""),
};
rec.message[..message.len()].copy_from_slice(message);
rec
}
}
impl fmt::Display for DiagnosticRecord {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let state = CStr::from_bytes_with_nul(&self.state).unwrap();
write!(
f,
"State: {}, Native error: {}, Message: {}",
state.to_str().unwrap(),
self.native_error,
self.message_string,
)
}
}
impl fmt::Debug for DiagnosticRecord {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl Error for DiagnosticRecord {
fn description(&self) -> &str {
&self.message_string
}
fn cause(&self) -> Option<& dyn Error> {
None
}
}
pub trait GetDiagRec {
fn get_diag_rec(&self, record_number: i16) -> Option<DiagnosticRecord>;
}
impl<D> GetDiagRec for D
where
D: safe::Diagnostics,
{
fn get_diag_rec(&self, record_number: i16) -> Option<DiagnosticRecord> {
use safe::ReturnOption::*;
let mut message = [0; MAX_DIAGNOSTIC_MESSAGE_SIZE];
match self.diagnostics(record_number, &mut message) {
Success(result) | Info(result) => {
let mut message_length = cmp::min(result.text_length, MAX_DIAGNOSTIC_MESSAGE_SIZE as ffi::SQLSMALLINT - 1);
while message_length > 0 && message[(message_length - 1) as usize] == 0 {
message_length = message_length - 1;
}
Some(DiagnosticRecord {
state: result.state,
native_error: result.native_error,
message_length,
message,
message_string: unsafe {
super::environment::OS_ENCODING.decode(&message[0..message_length as usize]).0.into_owned()
},
})
}
NoData(()) => None,
Error(()) => panic!("Diagnostics returned error for record number {}. Record numbers have to be at least 1.", record_number),
}
}
}
#[cfg(test)]
mod test {
use super::*;
impl DiagnosticRecord {
fn new() -> DiagnosticRecord {
DiagnosticRecord {
state: [0u8; ffi::SQL_SQLSTATE_SIZE + 1],
message: [0u8; MAX_DIAGNOSTIC_MESSAGE_SIZE],
native_error: 0,
message_length: 0,
message_string: String::from(""),
}
}
}
#[test]
fn formatting() {
let message = b"[Microsoft][ODBC Driver Manager] Function sequence error\0";
let mut rec = DiagnosticRecord::new();
rec.state = b"HY010\0".clone();
rec.message_string = CStr::from_bytes_with_nul(message).unwrap().to_str().unwrap().to_owned();
rec.message_length = 56;
for i in 0..(rec.message_length as usize) {
rec.message[i] = message[i];
}
assert_eq!(
format!("{}", rec),
"State: HY010, Native error: 0, Message: [Microsoft][ODBC Driver Manager] \
Function sequence error"
);
}
}