Skip to main content

odbc_api/handles/
environment.rs

1use super::{
2    Connection,
3    any_handle::AnyHandle,
4    drop_handle,
5    sql_char::SqlChar,
6    sql_result::{ExtSqlReturn, SqlResult},
7};
8use log::debug;
9use odbc_sys::{
10    AttrCpMatch, AttrOdbcVersion, EnvironmentAttribute, FetchOrientation, HEnv, Handle, HandleType,
11    SQLAllocHandle, SQLSetEnvAttr,
12};
13use std::ptr::null_mut;
14
15#[cfg(not(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows"))))]
16use odbc_sys::{SQLDataSources as sql_data_sources, SQLDrivers as sql_drivers};
17
18#[cfg(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows")))]
19use odbc_sys::{SQLDataSourcesW as sql_data_sources, SQLDriversW as sql_drivers};
20
21/// An `Environment` is a global context, in which to access data.
22///
23/// Associated with an `Environment` is any information that is global in nature, such as:
24///
25/// * The `Environment`'s state
26/// * The current environment-level diagnostics
27/// * The handles of connections currently allocated on the environment
28/// * The current setting of each environment attribute
29#[derive(Debug)]
30pub struct Environment {
31    /// Invariant: Should always point to a valid ODBC Environment
32    handle: HEnv,
33}
34
35/// See: <https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/multithreading>
36unsafe impl Send for Environment {}
37
38// We are not declaring Environment as Sync due to its interior mutability with regards to iterator
39// state and error handilng
40
41unsafe impl AnyHandle for Environment {
42    fn as_handle(&self) -> Handle {
43        self.handle.as_handle()
44    }
45
46    fn handle_type(&self) -> HandleType {
47        HandleType::Env
48    }
49}
50
51impl Drop for Environment {
52    fn drop(&mut self) {
53        unsafe {
54            drop_handle(self.handle.as_handle(), HandleType::Env);
55        }
56    }
57}
58
59impl Environment {
60    /// Enable or disable (default) connection pooling for ODBC connections. Call this function
61    /// before creating the ODBC environment for which you want to enable connection pooling.
62    ///
63    /// See:
64    /// <https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/driver-manager-connection-pooling>
65    ///
66    /// # Safety
67    ///
68    /// > An ODBC driver must be fully thread-safe, and connections must not have thread affinity to
69    /// > support connection pooling. This means the driver is able to handle a call on any thread
70    /// > at any time and is able to connect on one thread, to use the connection on another thread,
71    /// > and to disconnect on a third thread.
72    pub unsafe fn set_connection_pooling(scheme: odbc_sys::AttrConnectionPooling) -> SqlResult<()> {
73        unsafe {
74            SQLSetEnvAttr(
75                HEnv::null(),
76                odbc_sys::EnvironmentAttribute::ConnectionPooling,
77                scheme.into(),
78                odbc_sys::IS_INTEGER,
79            )
80        }
81        .into_sql_result("SQLSetEnvAttr")
82    }
83
84    pub fn set_connection_pooling_matching(&mut self, matching: AttrCpMatch) -> SqlResult<()> {
85        unsafe {
86            SQLSetEnvAttr(
87                self.handle,
88                odbc_sys::EnvironmentAttribute::CpMatch,
89                matching.into(),
90                odbc_sys::IS_INTEGER,
91            )
92        }
93        .into_sql_result("SQLSetEnvAttr")
94    }
95
96    /// An allocated ODBC Environment handle
97    pub fn new() -> SqlResult<Self> {
98        // After running a lot of unit tests in parallel on both linux and windows architectures and
99        // never seeing a race condition related to this I deem this safe. In the past I feared
100        // concurrent construction of multiple Environments might race on shared state. Mostly due
101        // to <https://github.com/Koka/odbc-rs/issues/29> and
102        // <http://old.vk.pp.ru/docs/sybase-any/interfaces/00000034.htm>. Since however I since
103        // however official sources imply it is ok for an application to have multiple environments
104        // and I did not get it to race ever on my machine.
105        unsafe {
106            let mut handle = Handle::null();
107            let result: SqlResult<()> =
108                SQLAllocHandle(HandleType::Env, Handle::null(), &mut handle)
109                    .into_sql_result("SQLAllocHandle");
110            result.on_success(|| Environment {
111                handle: handle.as_henv(),
112            })
113        }
114    }
115
116    /// Declares which Version of the ODBC API we want to use. This is the first thing that should
117    /// be done with any ODBC environment.
118    pub fn declare_version(&self, version: AttrOdbcVersion) -> SqlResult<()> {
119        unsafe {
120            SQLSetEnvAttr(
121                self.handle,
122                EnvironmentAttribute::OdbcVersion,
123                version.into(),
124                0,
125            )
126            .into_sql_result("SQLSetEnvAttr")
127        }
128    }
129
130    /// Allocate a new connection handle. The `Connection` must not outlive the `Environment`.
131    pub fn allocate_connection(&self) -> SqlResult<Connection<'_>> {
132        let mut handle = Handle::null();
133        unsafe {
134            SQLAllocHandle(HandleType::Dbc, self.as_handle(), &mut handle)
135                .into_sql_result("SQLAllocHandle")
136                .on_success(|| {
137                    let handle = handle.as_hdbc();
138                    #[cfg(not(feature = "structured_logging"))]
139                    debug!("SQLAllocHandle allocated connection (Dbc) handle '{handle:?}'");
140                    #[cfg(feature = "structured_logging")]
141                    debug!(
142                        target: "odbc_api",
143                        handle:? = handle;
144                        "Connection handle allocated"
145                    );
146                    Connection::new(handle)
147                })
148        }
149    }
150
151    /// Provides access to the raw ODBC environment handle.
152    pub fn as_raw(&self) -> HEnv {
153        self.handle
154    }
155
156    /// List drivers descriptions and driver attribute keywords. Returns `NoData` to indicate the
157    /// end of the list.
158    ///
159    /// # Safety
160    ///
161    /// Callers need to make sure only one thread is iterating over driver information at a time.
162    /// Method changes environment state. This method would be safe to call via an exclusive `&mut`
163    /// reference, yet that would restrict use cases. E.g. requesting information would only be
164    /// possible before connections borrow a reference.
165    ///
166    /// # Parameters
167    ///
168    /// * `direction`: Determines whether the Driver Manager fetches the next driver in the list
169    ///   ([`FetchOrientation::Next`]) or whether the search starts from the beginning of the list
170    ///   ([`FetchOrientation::First`]).
171    /// * `buffer_description`: In case `true` is returned this buffer is filled with the
172    ///   description of the driver.
173    /// * `buffer_attributes`: In case `true` is returned this buffer is filled with a list of key
174    ///   value attributes. E.g.: `"key1=value1\0key2=value2\0\0"`.
175    ///
176    ///  Use [`Environment::drivers_buffer_len`] to determine buffer lengths.
177    ///
178    /// See [SQLDrivers][1]
179    ///
180    /// [1]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqldrivers-function
181    pub unsafe fn drivers_buffer_fill(
182        &self,
183        direction: FetchOrientation,
184        buffer_description: &mut [SqlChar],
185        buffer_attributes: &mut [SqlChar],
186    ) -> SqlResult<()> {
187        unsafe {
188            sql_drivers(
189                self.handle,
190                direction,
191                buffer_description.as_mut_ptr(),
192                buffer_description.len().try_into().unwrap(),
193                null_mut(),
194                buffer_attributes.as_mut_ptr(),
195                buffer_attributes.len().try_into().unwrap(),
196                null_mut(),
197            )
198        }
199        .into_sql_result("SQLDrivers")
200    }
201
202    /// Use together with [`Environment::drivers_buffer_fill`] to list drivers descriptions and
203    /// driver attribute keywords.
204    ///
205    /// # Safety
206    ///
207    /// Callers need to make sure only one thread is iterating over driver information at a time.
208    /// Method changes environment state. This method would be safe to call via an exclusive `&mut`
209    /// reference, yet that would restrict use cases. E.g. requesting information would only be
210    /// possible before connections borrow a reference.
211    ///
212    /// # Parameters
213    ///
214    /// * `direction`: Determines whether the Driver Manager fetches the next driver in the list
215    ///   ([`FetchOrientation::Next`]) or whether the search starts from the beginning of the list
216    ///   ([`FetchOrientation::First`]).
217    ///
218    /// # Return
219    ///
220    /// `(driver description length, attribute length)`. Length is in characters minus terminating
221    /// terminating zero.
222    ///
223    /// See [SQLDrivers][1]
224    ///
225    /// [1]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqldrivers-function
226    pub unsafe fn drivers_buffer_len(&self, direction: FetchOrientation) -> SqlResult<(i16, i16)> {
227        // Lengths in characters minus terminating zero
228        let mut length_description: i16 = 0;
229        let mut length_attributes: i16 = 0;
230        // Determine required buffer size
231        unsafe {
232            sql_drivers(
233                self.handle,
234                direction,
235                null_mut(),
236                0,
237                &mut length_description,
238                null_mut(),
239                0,
240                &mut length_attributes,
241            )
242        }
243        .into_sql_result("SQLDrivers")
244        .on_success(|| (length_description, length_attributes))
245    }
246
247    /// Use together with [`Environment::data_source_buffer_fill`] to list drivers descriptions and
248    /// driver attribute keywords.
249    ///
250    /// # Safety
251    ///
252    /// Callers need to make sure only one thread is iterating over data source information at a
253    /// time. Method changes environment state. This method would be safe to call via an exclusive
254    /// `&mut` reference, yet that would restrict use cases. E.g. requesting information would only
255    /// be possible before connections borrow a reference.
256    ///
257    /// # Parameters
258    ///
259    /// * `direction`: Determines whether the Driver Manager fetches the next driver in the list
260    ///   ([`FetchOrientation::Next`]) or whether the search starts from the beginning of the list
261    ///   ([`FetchOrientation::First`], [`FetchOrientation::FirstSystem`],
262    ///   [`FetchOrientation::FirstUser`]).
263    ///
264    /// # Return
265    ///
266    /// `(server name length,  description length)`. Length is in characters minus terminating zero.
267    pub unsafe fn data_source_buffer_len(
268        &self,
269        direction: FetchOrientation,
270    ) -> SqlResult<(i16, i16)> {
271        // Lengths in characters minus terminating zero
272        let mut length_name: i16 = 0;
273        let mut length_description: i16 = 0;
274        // Determine required buffer size
275        unsafe {
276            sql_data_sources(
277                self.handle,
278                direction,
279                null_mut(),
280                0,
281                &mut length_name,
282                null_mut(),
283                0,
284                &mut length_description,
285            )
286        }
287        .into_sql_result("SQLDataSources")
288        .on_success(|| (length_name, length_description))
289    }
290
291    /// List drivers descriptions and driver attribute keywords.
292    ///
293    /// # Safety
294    ///
295    /// Callers need to make sure only one thread is iterating over data source information at a
296    /// time. Method changes environment state. This method would be safe to call via an exclusive
297    /// `&mut` reference, yet that would restrict use cases. E.g. requesting information would only
298    /// be possible before connections borrow a reference. [`SqlResult::NoData`]
299    ///
300    /// # Parameters
301    ///
302    /// * `direction`: Determines whether the Driver Manager fetches the next driver in the list
303    ///   ([`FetchOrientation::Next`]) or whether the search starts from the beginning of the list
304    ///   ([`FetchOrientation::First`], [`FetchOrientation::FirstSystem`],
305    ///   [`FetchOrientation::FirstUser`]).
306    /// * `buffer_name`: 0illed with the name of the datasource if available
307    /// * `buffer_description`: Filled with a description of the datasource (i.e. Driver name).
308    ///
309    ///  Use [`Environment::data_source_buffer_len`] to determine buffer lengths.
310    pub unsafe fn data_source_buffer_fill(
311        &self,
312        direction: FetchOrientation,
313        buffer_name: &mut [SqlChar],
314        buffer_description: &mut [SqlChar],
315    ) -> SqlResult<()> {
316        unsafe {
317            sql_data_sources(
318                self.handle,
319                direction,
320                buffer_name.as_mut_ptr(),
321                buffer_name.len().try_into().unwrap(),
322                null_mut(),
323                buffer_description.as_mut_ptr(),
324                buffer_description.len().try_into().unwrap(),
325                null_mut(),
326            )
327        }
328        .into_sql_result("SQLDataSources")
329    }
330}