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}