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}