1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
use super::*;
use sys::*;
/// A buffer large enough to hold an `SOLState` for diagnostics and a terminating zero.
pub type State = [SQLCHAR; SQL_SQLSTATE_SIZE + 1];

/// Result of `Diagnostics::diagnostics`
#[derive(Debug, Clone, Copy)]
pub struct DiagResult {
    /// A five-character SQLSTATE code (and terminating NULL) for the diagnostic record
    /// `rec_number`. The first two characters indicate the class; the next three indicate the
    /// subclass. For more information, see [SQLSTATE][1]s.
    /// [1]: https://docs.microsoft.com/sql/odbc/reference/develop-app/sqlstates
    pub state: State,
    /// Native error code specific to the data source.
    pub native_error: SQLINTEGER,
    /// The total number of characters (excluding the terminating NULL) available to return in
    /// `message_text`.
    pub text_length: SQLSMALLINT,
}

/// A type implementing this trait is able to provide diagnostic information regarding the last
/// method call.
pub trait Diagnostics {
    /// Returns the current values of multiple fields of a diagnostic record that contains error,
    /// warning, and status information.
    ///
    /// # Arguments
    ///
    /// * `rec_number` - Indicates the status record from which the application seeks information.
    ///                  Status records are numbered from 1.
    /// * `message_text` - Buffer in which to return the diagnostic message text string. If the
    ///                    number of characters to return is greater than the buffer length, the
    ///                    diagnostic message is truncated to `max(message_text.len() - 1, 0)`. For
    ///                    the format of the string, see [Diagnostic Messages][1]
    ///
    /// # Result
    ///`
    /// * `Success` - The function successfully returned diagnostic information.
    /// * `Info` - The `message_text` buffer was too small to hold the requested diagnostic message.
    ///            No diagnostic records were generated. To determine that a truncation occurred,
    ///            the application must compare the buffer length to the actual number of bytes
    ///            available, which is found in `DiagResult::text_length`
    /// * `Error` - `rec_number` was negative or `0`.
    /// * `NoData` - `rec_number` was greater than the number of diagnostic records that existed
    ///              for the specified Handle. The function also returns `NoData` for any positive
    ///              `rec_number` if there are no diagnostic records available.
    /// [1]: https://docs.microsoft.com/sql/odbc/reference/develop-app/diagnostic-messages
    fn diagnostics(
        &self,
        rec_number: SQLSMALLINT,
        message_text: &mut [SQLCHAR],
    ) -> ReturnOption<DiagResult>;
}

impl<H: Handle> Diagnostics for H {
    fn diagnostics(
        &self,
        rec_number: SQLSMALLINT,
        message_text: &mut [SQLCHAR],
    ) -> ReturnOption<DiagResult> {
        unsafe {
            let mut text_length = 0;
            let mut state = [0; 6];
            let mut native_error = 0;
            let ret = SQLGetDiagRec(
                H::HANDLE_TYPE,
                self.handle(),
                rec_number,
                state.as_mut_ptr(),
                &mut native_error,
                message_text.as_mut_ptr(),
                message_text.buf_len(),
                &mut text_length,
            );
            let result = DiagResult {
                text_length: text_length,
                state: state,
                native_error: native_error,
            };
            match ret {
                SQL_SUCCESS => ReturnOption::Success(result),
                SQL_SUCCESS_WITH_INFO => ReturnOption::Info(result),
                SQL_ERROR => ReturnOption::Error(()),
                SQL_NO_DATA => ReturnOption::NoData(()),
                unexpected => panic!("SQLGetDiagRec returned: {:?}", unexpected),
            }
        }
    }
}

impl<S, E> Diagnostics for Return<S, E>
where
    S: Diagnostics,
    E: Diagnostics,
{
    fn diagnostics(
        &self,
        rec_number: SQLSMALLINT,
        message_text: &mut [SQLCHAR],
    ) -> ReturnOption<DiagResult> {
        match *self {
            Success(ref s) | Info(ref s) => s.diagnostics(rec_number, message_text),
            Error(ref e) => e.diagnostics(rec_number, message_text),
        }
    }
}