odbc_api/handles/
logging.rs

1use super::{Diagnostics, Record};
2use log::{warn, Level};
3
4/// This function inspects all the diagnostics of an ODBC handle and logs their text messages. It
5/// is going to print placeholder characters, if it cannot convert the message to UTF-8.
6pub fn log_diagnostics(handle: &(impl Diagnostics + ?Sized)) {
7    if log::max_level() < Level::Warn {
8        // Early return to safe work creating all these log records in case we would not log
9        // anything.
10        return;
11    }
12
13    let mut rec = Record::with_capacity(512);
14    let mut rec_number = 1;
15
16    // Log results, while there are diagnostic records
17    while rec.fill_from(handle, rec_number) {
18        warn!("{}", rec);
19        // Prevent overflow. This is not that unlikely to happen, since some `execute` or `fetch`
20        // calls can cause diagnostic messages for each row
21        if rec_number == i16::MAX {
22            warn!("Too many diagnostic records were generated. Not all could be logged.");
23            break;
24        }
25        rec_number += 1;
26    }
27}
28
29#[cfg(test)]
30mod tests {
31    use std::{cell::RefCell, cmp::max};
32
33    use crate::handles::{diagnostics::DiagnosticResult, SqlChar, State};
34
35    use super::{log_diagnostics, Diagnostics};
36
37    struct InfiniteDiagnostics {
38        times_called: RefCell<usize>,
39    }
40
41    impl InfiniteDiagnostics {
42        fn new() -> InfiniteDiagnostics {
43            Self {
44                times_called: RefCell::new(0),
45            }
46        }
47
48        fn num_calls(&self) -> usize {
49            *self.times_called.borrow()
50        }
51    }
52
53    impl Diagnostics for InfiniteDiagnostics {
54        fn diagnostic_record(
55            &self,
56            _rec_number: i16,
57            _message_text: &mut [SqlChar],
58        ) -> Option<DiagnosticResult> {
59            *self.times_called.borrow_mut() += 1;
60            Some(DiagnosticResult {
61                state: State([0, 0, 0, 0, 0]),
62                native_error: 0,
63                text_length: 0,
64            })
65        }
66    }
67
68    /// This test is inspired by a bug caused from a fetch statement generating a lot of diagnostic
69    /// messages.
70    #[test]
71    fn more_than_i16_max_diagnostic_records() {
72        // Ensure log level is at least warn for test to work
73        log::set_max_level(max(log::LevelFilter::Warn, log::max_level()));
74
75        let spy = InfiniteDiagnostics::new();
76        // We are quite happy if this terminates and does not overflow
77        log_diagnostics(&spy);
78
79        assert_eq!(spy.num_calls(), i16::MAX as usize)
80    }
81}