odbc_api/handles/
environment.rs

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