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}