odbc_iter/
thread_local.rs

1use log::debug;
2use std::cell::RefCell;
3
4use crate::query::Connection;
5use crate::{Odbc, OdbcError};
6
7thread_local! {
8    static DB: RefCell<Option<Connection>> = RefCell::new(None);
9}
10
11/// Access to thread local connection.
12///
13/// Provided closure will receive the `Connection` object and may return it for reuse by another call or drop it to force new connection to be established to database on next call.
14///
15/// If there was an error during connection establishment it is provided to the closure. Next call will attempt to connect again and a new error may be provided.
16///
17/// `connection_string` is used only when making new `Connection` object initially, after error or after old `Connection` object was dropped.
18pub fn connection_with<O, F>(
19    connection_string: &str,
20    f: F
21) -> O where F: Fn(Result<Connection, OdbcError>) -> (Option<Connection>, O) {
22    initialized_connection_with(connection_string, |_| Ok(()), f)
23}
24
25/// Access to thread local connection with connection initialization.
26///
27/// Like `connection_with` but also takes `init` closure that is executed once when new connection was
28/// successfully established. This allows for execution of connection configuration queries.
29///
30/// If `init` returns an error it is passed to the second closure and the connection will be dropped.
31pub fn initialized_connection_with<O, E, I, F>(
32    connection_string: &str,
33    init: I,
34    f: F
35) -> O where E: From<OdbcError>, I: Fn(&mut Connection) -> Result<(), E>, F: Fn(Result<Connection, E>) -> (Option<Connection>, O) {
36    DB.with(|db| {
37        let connection;
38
39        let conn = db.borrow_mut().take();
40        match conn {
41            Some(conn) => connection = conn,
42            None => {
43                let id = std::thread::current().id();
44                debug!("[{:?}] Connecting to database: {}", id, &connection_string);
45
46                match Odbc::connect(&connection_string)
47                    .map_err(Into::into)
48                    .and_then(|mut conn| init(&mut conn).map(|_| conn)) {
49                    Ok(conn) => {
50                        connection = conn;
51                    }
52                    Err(err) => return f(Err(err)).1,
53                }
54            }
55        }
56
57        let (connection, o) = f(Ok(connection));
58        *db.borrow_mut() = connection;
59        o
60    })
61}
62
63#[cfg(test)]
64mod tests {
65    #[allow(unused_imports)]
66    use super::*;
67    #[allow(unused_imports)]
68    use crate::*;
69    #[allow(unused_imports)]
70    use assert_matches::assert_matches;
71
72    #[cfg(feature = "test-monetdb")]
73    #[test]
74    fn test_connection_with() {
75        connection_with(
76            crate::tests::monetdb_connection_string().as_str(),
77            |result| {
78                let mut monetdb = result.expect("connect to MonetDB");
79                let data = monetdb
80                    .handle()
81                    .query::<ValueRow>("SELECT 'foo'")
82                    .expect("failed to run query")
83                    .collect::<Result<Vec<_>, _>>()
84                    .expect("fetch data");
85
86                assert_matches!(data[0][0], Some(Value::String(ref string)) => assert_eq!(string, "foo"));
87                (Some(monetdb), ())
88            },
89        )
90    }
91
92    #[cfg(feature = "test-monetdb")]
93    #[test]
94    fn test_connection_with_reconnect() {
95        connection_with(
96            crate::tests::monetdb_connection_string().as_str(),
97            |result| {
98                let mut monetdb = result.expect("connect to MonetDB");
99                let data = monetdb
100                    .handle()
101                    .query::<ValueRow>("SELECT 'foo'")
102                    .expect("failed to run query")
103                    .collect::<Result<Vec<_>, _>>()
104                    .expect("fetch data");
105
106                assert_matches!(data[0][0], Some(Value::String(ref string)) => assert_eq!(string, "foo"));
107                (None, ())
108            },
109        );
110
111        connection_with(
112            crate::tests::monetdb_connection_string().as_str(),
113            |result| {
114                let mut monetdb = result.expect("connect to MonetDB");
115                let data = monetdb
116                    .handle()
117                    .query::<ValueRow>("SELECT 'foo'")
118                    .expect("failed to run query")
119                    .collect::<Result<Vec<_>, _>>()
120                    .expect("fetch data");
121
122                assert_matches!(data[0][0], Some(Value::String(ref string)) => assert_eq!(string, "foo"));
123                (None, ())
124            },
125        )
126    }
127
128    #[cfg(feature = "test-monetdb")]
129    #[test]
130    fn test_connection_with_nested() {
131        connection_with(
132            crate::tests::monetdb_connection_string().as_str(),
133            |result| {
134                let mut monetdb = result.expect("connect to MonetDB");
135                let data = monetdb
136                    .handle()
137                    .query::<ValueRow>("SELECT 'foo'")
138                    .expect("failed to run query")
139                    .collect::<Result<Vec<_>, _>>()
140                    .expect("fetch data");
141
142                assert_matches!(data[0][0], Some(Value::String(ref string)) => assert_eq!(string, "foo"));
143
144                connection_with(
145                    crate::tests::monetdb_connection_string().as_str(),
146                    |result| {
147                        let mut monetdb = result.expect("connect to MonetDB");
148                        let data = monetdb
149                            .handle()
150                            .query::<ValueRow>("SELECT 'foo'")
151                            .expect("failed to run query")
152                            .collect::<Result<Vec<_>, _>>()
153                            .expect("fetch data");
154
155                        assert_matches!(data[0][0], Some(Value::String(ref string)) => assert_eq!(string, "foo"));
156                        (Some(monetdb), ())
157                    },
158                );
159
160                (Some(monetdb), ())
161            },
162        )
163    }
164}