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                    debug!("SQLAllocHandle allocated connection (Dbc) handle '{handle:?}'");
139                    Connection::new(handle)
140                })
141        }
142    }
143
144    /// Provides access to the raw ODBC environment handle.
145    pub fn as_raw(&self) -> HEnv {
146        self.handle
147    }
148
149    /// List drivers descriptions and driver attribute keywords. Returns `NoData` to indicate the
150    /// end of the list.
151    ///
152    /// # Safety
153    ///
154    /// Callers need to make sure only one thread is iterating over driver information at a time.
155    /// Method changes environment state. This method would be safe to call via an exclusive `&mut`
156    /// reference, yet that would restrict use cases. E.g. requesting information would only be
157    /// possible before connections borrow a reference.
158    ///
159    /// # Parameters
160    ///
161    /// * `direction`: Determines whether the Driver Manager fetches the next driver in the list
162    ///   ([`FetchOrientation::Next`]) or whether the search starts from the beginning of the list
163    ///   ([`FetchOrientation::First`]).
164    /// * `buffer_description`: In case `true` is returned this buffer is filled with the
165    ///   description of the driver.
166    /// * `buffer_attributes`: In case `true` is returned this buffer is filled with a list of key
167    ///   value attributes. E.g.: `"key1=value1\0key2=value2\0\0"`.
168    ///
169    ///  Use [`Environment::drivers_buffer_len`] to determine buffer lengths.
170    ///
171    /// See [SQLDrivers][1]
172    ///
173    /// [1]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqldrivers-function
174    pub unsafe fn drivers_buffer_fill(
175        &self,
176        direction: FetchOrientation,
177        buffer_description: &mut [SqlChar],
178        buffer_attributes: &mut [SqlChar],
179    ) -> SqlResult<()> {
180        unsafe {
181            sql_drivers(
182                self.handle,
183                direction,
184                buffer_description.as_mut_ptr(),
185                buffer_description.len().try_into().unwrap(),
186                null_mut(),
187                buffer_attributes.as_mut_ptr(),
188                buffer_attributes.len().try_into().unwrap(),
189                null_mut(),
190            )
191        }
192        .into_sql_result("SQLDrivers")
193    }
194
195    /// Use together with [`Environment::drivers_buffer_fill`] to list drivers descriptions and
196    /// driver attribute keywords.
197    ///
198    /// # Safety
199    ///
200    /// Callers need to make sure only one thread is iterating over driver information at a time.
201    /// Method changes environment state. This method would be safe to call via an exclusive `&mut`
202    /// reference, yet that would restrict use cases. E.g. requesting information would only be
203    /// possible before connections borrow a reference.
204    ///
205    /// # Parameters
206    ///
207    /// * `direction`: Determines whether the Driver Manager fetches the next driver in the list
208    ///   ([`FetchOrientation::Next`]) or whether the search starts from the beginning of the list
209    ///   ([`FetchOrientation::First`]).
210    ///
211    /// # Return
212    ///
213    /// `(driver description length, attribute length)`. Length is in characters minus terminating
214    /// terminating zero.
215    ///
216    /// See [SQLDrivers][1]
217    ///
218    /// [1]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqldrivers-function
219    pub unsafe fn drivers_buffer_len(&self, direction: FetchOrientation) -> SqlResult<(i16, i16)> {
220        // Lengths in characters minus terminating zero
221        let mut length_description: i16 = 0;
222        let mut length_attributes: i16 = 0;
223        // Determine required buffer size
224        unsafe {
225            sql_drivers(
226                self.handle,
227                direction,
228                null_mut(),
229                0,
230                &mut length_description,
231                null_mut(),
232                0,
233                &mut length_attributes,
234            )
235        }
236        .into_sql_result("SQLDrivers")
237        .on_success(|| (length_description, length_attributes))
238    }
239
240    /// Use together with [`Environment::data_source_buffer_fill`] to list drivers descriptions and
241    /// driver attribute keywords.
242    ///
243    /// # Safety
244    ///
245    /// Callers need to make sure only one thread is iterating over data source information at a
246    /// time. Method changes environment state. This method would be safe to call via an exclusive
247    /// `&mut` reference, yet that would restrict use cases. E.g. requesting information would only
248    /// be possible before connections borrow a reference.
249    ///
250    /// # Parameters
251    ///
252    /// * `direction`: Determines whether the Driver Manager fetches the next driver in the list
253    ///   ([`FetchOrientation::Next`]) or whether the search starts from the beginning of the list
254    ///   ([`FetchOrientation::First`], [`FetchOrientation::FirstSystem`],
255    ///   [`FetchOrientation::FirstUser`]).
256    ///
257    /// # Return
258    ///
259    /// `(server name length,  description length)`. Length is in characters minus terminating zero.
260    pub unsafe fn data_source_buffer_len(
261        &self,
262        direction: FetchOrientation,
263    ) -> SqlResult<(i16, i16)> {
264        // Lengths in characters minus terminating zero
265        let mut length_name: i16 = 0;
266        let mut length_description: i16 = 0;
267        // Determine required buffer size
268        unsafe {
269            sql_data_sources(
270                self.handle,
271                direction,
272                null_mut(),
273                0,
274                &mut length_name,
275                null_mut(),
276                0,
277                &mut length_description,
278            )
279        }
280        .into_sql_result("SQLDataSources")
281        .on_success(|| (length_name, length_description))
282    }
283
284    /// List drivers descriptions and driver attribute keywords.
285    ///
286    /// # Safety
287    ///
288    /// Callers need to make sure only one thread is iterating over data source information at a
289    /// time. Method changes environment state. This method would be safe to call via an exclusive
290    /// `&mut` reference, yet that would restrict use cases. E.g. requesting information would only
291    /// be possible before connections borrow a reference. [`SqlResult::NoData`]
292    ///
293    /// # Parameters
294    ///
295    /// * `direction`: Determines whether the Driver Manager fetches the next driver in the list
296    ///   ([`FetchOrientation::Next`]) or whether the search starts from the beginning of the list
297    ///   ([`FetchOrientation::First`], [`FetchOrientation::FirstSystem`],
298    ///   [`FetchOrientation::FirstUser`]).
299    /// * `buffer_name`: 0illed with the name of the datasource if available
300    /// * `buffer_description`: Filled with a description of the datasource (i.e. Driver name).
301    ///
302    ///  Use [`Environment::data_source_buffer_len`] to determine buffer lengths.
303    pub unsafe fn data_source_buffer_fill(
304        &self,
305        direction: FetchOrientation,
306        buffer_name: &mut [SqlChar],
307        buffer_description: &mut [SqlChar],
308    ) -> SqlResult<()> {
309        unsafe {
310            sql_data_sources(
311                self.handle,
312                direction,
313                buffer_name.as_mut_ptr(),
314                buffer_name.len().try_into().unwrap(),
315                null_mut(),
316                buffer_description.as_mut_ptr(),
317                buffer_description.len().try_into().unwrap(),
318                null_mut(),
319            )
320        }
321        .into_sql_result("SQLDataSources")
322    }
323}