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}