odbc_api/handles/
environment.rs

1use super::{
2    as_handle::AsHandle,
3    drop_handle,
4    sql_char::SqlChar,
5    sql_result::{ExtSqlReturn, SqlResult},
6    Connection,
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        SQLSetEnvAttr(
74            null_mut(),
75            odbc_sys::EnvironmentAttribute::ConnectionPooling,
76            scheme.into(),
77            odbc_sys::IS_INTEGER,
78        )
79        .into_sql_result("SQLSetEnvAttr")
80    }
81
82    pub fn set_connection_pooling_matching(&mut self, matching: AttrCpMatch) -> SqlResult<()> {
83        unsafe {
84            SQLSetEnvAttr(
85                self.handle,
86                odbc_sys::EnvironmentAttribute::CpMatch,
87                matching.into(),
88                odbc_sys::IS_INTEGER,
89            )
90        }
91        .into_sql_result("SQLSetEnvAttr")
92    }
93
94    /// An allocated ODBC Environment handle
95    pub fn new() -> SqlResult<Self> {
96        // After running a lot of unit tests in parallel on both linux and windows architectures and
97        // never seeing a race condition related to this I deem this safe. In the past I feared
98        // concurrent construction of multiple Environments might race on shared state. Mostly due
99        // to <https://github.com/Koka/odbc-rs/issues/29> and
100        // <http://old.vk.pp.ru/docs/sybase-any/interfaces/00000034.htm>. Since however I since
101        // however official sources imply it is ok for an application to have multiple environments
102        // and I did not get it to race ever on my machine.
103        unsafe {
104            let mut handle = null_mut();
105            let result: SqlResult<()> = SQLAllocHandle(HandleType::Env, null_mut(), &mut handle)
106                .into_sql_result("SQLAllocHandle");
107            result.on_success(|| Environment {
108                handle: handle as HEnv,
109            })
110        }
111    }
112
113    /// Declares which Version of the ODBC API we want to use. This is the first thing that should
114    /// be done with any ODBC environment.
115    pub fn declare_version(&self, version: AttrOdbcVersion) -> SqlResult<()> {
116        unsafe {
117            SQLSetEnvAttr(
118                self.handle,
119                EnvironmentAttribute::OdbcVersion,
120                version.into(),
121                0,
122            )
123            .into_sql_result("SQLSetEnvAttr")
124        }
125    }
126
127    /// Allocate a new connection handle. The `Connection` must not outlive the `Environment`.
128    pub fn allocate_connection(&self) -> SqlResult<Connection<'_>> {
129        let mut handle = null_mut();
130        unsafe {
131            SQLAllocHandle(HandleType::Dbc, self.as_handle(), &mut handle)
132                .into_sql_result("SQLAllocHandle")
133                .on_success(|| {
134                    let handle = handle as HDbc;
135                    debug!("SQLAllocHandle allocated connection (Dbc) handle '{handle:?}'");
136                    Connection::new(handle)
137                })
138        }
139    }
140
141    /// Provides access to the raw ODBC environment handle.
142    pub fn as_raw(&self) -> HEnv {
143        self.handle
144    }
145
146    /// List drivers descriptions and driver attribute keywords. Returns `NoData` to indicate the
147    /// end of the list.
148    ///
149    /// # Safety
150    ///
151    /// Callers need to make sure only one thread is iterating over driver information at a time.
152    /// Method changes environment state. This method would be safe to call via an exclusive `&mut`
153    /// reference, yet that would restrict use cases. E.g. requesting information would only be
154    /// possible before connections borrow a reference.
155    ///
156    /// # Parameters
157    ///
158    /// * `direction`: Determines whether the Driver Manager fetches the next driver in the list
159    ///   ([`FetchOrientation::Next`]) or whether the search starts from the beginning of the list
160    ///   ([`FetchOrientation::First`]).
161    /// * `buffer_description`: In case `true` is returned this buffer is filled with the
162    ///   description of the driver.
163    /// * `buffer_attributes`: In case `true` is returned this buffer is filled with a list of
164    ///   key value attributes. E.g.: `"key1=value1\0key2=value2\0\0"`.
165    ///
166    ///  Use [`Environment::drivers_buffer_len`] to determine buffer lengths.
167    ///
168    /// See [SQLDrivers][1]
169    ///
170    /// [1]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqldrivers-function
171    pub unsafe fn drivers_buffer_fill(
172        &self,
173        direction: FetchOrientation,
174        buffer_description: &mut [SqlChar],
175        buffer_attributes: &mut [SqlChar],
176    ) -> SqlResult<()> {
177        sql_drivers(
178            self.handle,
179            direction,
180            buffer_description.as_mut_ptr(),
181            buffer_description.len().try_into().unwrap(),
182            null_mut(),
183            buffer_attributes.as_mut_ptr(),
184            buffer_attributes.len().try_into().unwrap(),
185            null_mut(),
186        )
187        .into_sql_result("SQLDrivers")
188    }
189
190    /// Use together with [`Environment::drivers_buffer_fill`] to list drivers descriptions and
191    /// driver attribute keywords.
192    ///
193    /// # Safety
194    ///
195    /// Callers need to make sure only one thread is iterating over driver information at a time.
196    /// Method changes environment state. This method would be safe to call via an exclusive `&mut`
197    /// reference, yet that would restrict use cases. E.g. requesting information would only be
198    /// possible before connections borrow a reference.
199    ///
200    /// # Parameters
201    ///
202    /// * `direction`: Determines whether the Driver Manager fetches the next driver in the list
203    ///   ([`FetchOrientation::Next`]) or whether the search starts from the beginning of the list
204    ///   ([`FetchOrientation::First`]).
205    ///
206    /// # Return
207    ///
208    /// `(driver description length, attribute length)`. Length is in characters minus terminating
209    /// terminating zero.
210    ///
211    /// See [SQLDrivers][1]
212    ///
213    /// [1]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqldrivers-function
214    pub unsafe fn drivers_buffer_len(&self, direction: FetchOrientation) -> SqlResult<(i16, i16)> {
215        // Lengths in characters minus terminating zero
216        let mut length_description: i16 = 0;
217        let mut length_attributes: i16 = 0;
218        // Determine required buffer size
219        sql_drivers(
220            self.handle,
221            direction,
222            null_mut(),
223            0,
224            &mut length_description,
225            null_mut(),
226            0,
227            &mut length_attributes,
228        )
229        .into_sql_result("SQLDrivers")
230        .on_success(|| (length_description, length_attributes))
231    }
232
233    /// Use together with [`Environment::data_source_buffer_fill`] to list drivers descriptions and
234    /// driver attribute keywords.
235    ///
236    /// # Safety
237    ///
238    /// Callers need to make sure only one thread is iterating over data source information at a
239    /// time. Method changes environment state. This method would be safe to call via an exclusive
240    /// `&mut` reference, yet that would restrict use cases. E.g. requesting information would only
241    /// be possible before connections borrow a reference.
242    ///
243    /// # Parameters
244    ///
245    /// * `direction`: Determines whether the Driver Manager fetches the next driver in the list
246    ///   ([`FetchOrientation::Next`]) or whether the search starts from the beginning of the list
247    ///   ([`FetchOrientation::First`], [`FetchOrientation::FirstSystem`],
248    ///   [`FetchOrientation::FirstUser`]).
249    ///
250    /// # Return
251    ///
252    /// `(server name length,  description length)`. Length is in characters minus terminating zero.
253    pub unsafe fn data_source_buffer_len(
254        &self,
255        direction: FetchOrientation,
256    ) -> SqlResult<(i16, i16)> {
257        // Lengths in characters minus terminating zero
258        let mut length_name: i16 = 0;
259        let mut length_description: i16 = 0;
260        // Determine required buffer size
261        sql_data_sources(
262            self.handle,
263            direction,
264            null_mut(),
265            0,
266            &mut length_name,
267            null_mut(),
268            0,
269            &mut length_description,
270        )
271        .into_sql_result("SQLDataSources")
272        .on_success(|| (length_name, length_description))
273    }
274
275    /// List drivers descriptions and driver attribute keywords.
276    ///
277    /// # Safety
278    ///
279    /// Callers need to make sure only one thread is iterating over data source information at a
280    /// time. Method changes environment state. This method would be safe to call via an exclusive
281    /// `&mut` reference, yet that would restrict use cases. E.g. requesting information would only
282    /// be possible before connections borrow a reference. [`SqlResult::NoData`]
283    ///
284    /// # Parameters
285    ///
286    /// * `direction`: Determines whether the Driver Manager fetches the next driver in the list
287    ///   ([`FetchOrientation::Next`]) or whether the search starts from the beginning of the list
288    ///   ([`FetchOrientation::First`], [`FetchOrientation::FirstSystem`],
289    ///   [`FetchOrientation::FirstUser`]).
290    /// * `buffer_name`: 0illed with the name of the datasource if available
291    /// * `buffer_description`: Filled with a description of the datasource (i.e. Driver name).
292    ///
293    ///  Use [`Environment::data_source_buffer_len`] to determine buffer lengths.
294    pub unsafe fn data_source_buffer_fill(
295        &self,
296        direction: FetchOrientation,
297        buffer_name: &mut [SqlChar],
298        buffer_description: &mut [SqlChar],
299    ) -> SqlResult<()> {
300        sql_data_sources(
301            self.handle,
302            direction,
303            buffer_name.as_mut_ptr(),
304            buffer_name.len().try_into().unwrap(),
305            null_mut(),
306            buffer_description.as_mut_ptr(),
307            buffer_description.len().try_into().unwrap(),
308            null_mut(),
309        )
310        .into_sql_result("SQLDataSources")
311    }
312}