1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
use super::{ as_handle::AsHandle, drop_handle, error::IntoResult, logging::log_diagnostics, Connection, Error, }; use log::debug; use odbc_sys::{ AttrOdbcVersion, EnvironmentAttribute, FetchOrientation, HDbc, HEnv, Handle, HandleType, SQLAllocHandle, SQLDataSourcesW, SQLDriversW, SQLSetEnvAttr, SqlReturn, }; use std::{convert::TryInto, ptr::null_mut}; /// An `Environment` is a global context, in which to access data. /// /// Associated with an `Environment` is any information that is global in nature, such as: /// /// * The `Environment`'s state /// * The current environment-level diagnostics /// * The handles of connections currently allocated on the environment /// * The current stetting of each environment attribute #[derive(Debug)] pub struct Environment { /// Invariant: Should always point to a valid ODBC Environment handle: HEnv, } /// See: <https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/multithreading?view=sql-server-ver15> unsafe impl Send for Environment {} /// See: <https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/multithreading?view=sql-server-ver15> unsafe impl Sync for Environment {} unsafe impl AsHandle for Environment { fn as_handle(&self) -> Handle { self.handle as Handle } fn handle_type(&self) -> HandleType { HandleType::Env } } impl Drop for Environment { fn drop(&mut self) { unsafe { drop_handle(self.handle as Handle, HandleType::Env); } } } impl Environment { /// An allocated ODBC Environment handle /// /// # Safety /// /// There may only be one Odbc environment in any process at any time. Take care using this /// function in unit tests, as these run in parallel by default in Rust. Also no library should /// probably wrap the creation of an odbc environment into a safe function call. This is because /// using two of these "safe" libraries at the same time in different parts of your program may /// lead to race condition thus violating Rust's safety guarantees. /// /// Creating one environment in your binary is safe however. pub unsafe fn new() -> Result<Self, Error> { let mut handle = null_mut(); let (handle, info) = match SQLAllocHandle(HandleType::Env, null_mut(), &mut handle) { // We can't provide nay diagnostics, as we don't have SqlReturn::ERROR => return Err(Error::NoDiagnostics), SqlReturn::SUCCESS => (handle, false), SqlReturn::SUCCESS_WITH_INFO => (handle, true), other => panic!( "Unexpected Return value for allocating ODBC Environment: {:?}", other ), }; debug!("ODBC Environment created."); let env = Environment { handle: handle as HEnv, }; if info { log_diagnostics(&env); } Ok(env) } /// Declares which Version of the ODBC API we want to use. This is the first thing that should /// be done with any ODBC environment. pub fn declare_version(&self, version: AttrOdbcVersion) -> Result<(), Error> { unsafe { SQLSetEnvAttr( self.handle, EnvironmentAttribute::OdbcVersion, version.into(), 0, ) .into_result(self) } } /// Allocate a new connection handle. The `Connection` must not outlive the `Environment`. pub fn allocate_connection(&self) -> Result<Connection, Error> { let mut handle = null_mut(); unsafe { SQLAllocHandle(HandleType::Dbc, self.as_handle(), &mut handle).into_result(self)?; Ok(Connection::new(handle as HDbc)) } } /// Provides access to the raw ODBC environment handle. pub fn as_raw(&self) -> HEnv { self.handle } /// List drivers descriptions and driver attribute keywords. /// /// # Parameters /// /// * `direction`: Determines whether the Driver Manager fetches the next driver in the list /// ([`FetchOrientation::Next`]) or whether the search starts from the beginning of the list /// ([`FetchOrientation::First`]). /// * `buffer_description`: In case `true` is returned this buffer is filled with the /// description of the driver. /// * `buffer_attributes`: In case `true` is returned this buffer is filled with a list of /// key value attributes. E.g.: `"key1=value1\0key2=value2\0\0"`. /// /// Use [`Environment::drivers_buffer_len`] to determine buffer lengths. /// /// See [SQLDrivers][1] /// /// [1]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqldrivers-function pub fn drivers_buffer_fill( &mut self, direction: FetchOrientation, buffer_description: &mut Vec<u16>, buffer_attributes: &mut Vec<u16>, ) -> Result<bool, Error> { // Use full capacity buffer_description.resize(buffer_description.capacity(), 0); buffer_attributes.resize(buffer_attributes.capacity(), 0); unsafe { match SQLDriversW( self.handle, direction, buffer_description.as_mut_ptr(), buffer_description.len().try_into().unwrap(), null_mut(), buffer_attributes.as_mut_ptr(), buffer_attributes.len().try_into().unwrap(), null_mut(), ) { SqlReturn::NO_DATA => Ok(false), other => { other.into_result(self)?; Ok(true) } } } } /// Use together with [`Environment::drivers_buffer_fill`] to list drivers descriptions and driver attribute /// keywords. /// /// # Parameters /// /// * `direction`: Determines whether the Driver Manager fetches the next driver in the list /// ([`FetchOrientation::Next`]) or whether the search starts from the beginning of the list /// ([`FetchOrientation::First`]). /// /// # Return /// /// `(driver description length, attribute length)`. Length is in characters minus terminating /// terminating zero. /// /// See [SQLDrivers][1] /// /// [1]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqldrivers-function pub fn drivers_buffer_len( &mut self, direction: FetchOrientation, ) -> Result<Option<(i16, i16)>, Error> { // Lengths in characters minus terminating zero let mut length_description: i16 = 0; let mut length_attributes: i16 = 0; unsafe { // Determine required buffer size match SQLDriversW( self.handle, direction, null_mut(), 0, &mut length_description, null_mut(), 0, &mut length_attributes, ) { SqlReturn::NO_DATA => return Ok(None), other => other.into_result(self)?, } Ok(Some((length_description, length_attributes))) } } /// Use together with [`Environment::data_source_buffer_fill`] to list drivers descriptions and /// driver attribute keywords. /// /// # Parameters /// /// * `direction`: Determines whether the Driver Manager fetches the next driver in the list /// ([`FetchOrientation::Next`]) or whether the search starts from the beginning of the list /// ([`FetchOrientation::First`], [`FetchOrientation::FirstSystem`], /// [`FetchOrientation::FirstUser`]). /// /// # Return /// /// `(server name length, description length)`. Length is in characters minus terminating zero. pub fn data_source_buffer_len( &mut self, direction: FetchOrientation, ) -> Result<Option<(i16, i16)>, Error> { // Lengths in characters minus terminating zero let mut length_name: i16 = 0; let mut length_description: i16 = 0; unsafe { // Determine required buffer size match odbc_sys::SQLDataSourcesW( self.handle, direction, null_mut(), 0, &mut length_name, null_mut(), 0, &mut length_description, ) { SqlReturn::NO_DATA => return Ok(None), other => other.into_result(self)?, } Ok(Some((length_name, length_description))) } } /// List drivers descriptions and driver attribute keywords. /// /// # Parameters /// /// * `direction`: Determines whether the Driver Manager fetches the next driver in the list /// ([`FetchOrientation::Next`]) or whether the search starts from the beginning of the list /// ([`FetchOrientation::First`], [`FetchOrientation::FirstSystem`], /// [`FetchOrientation::FirstUser`]). /// * `buffer_name`: In case `true` is returned this buffer is filled with the name of the /// datasource. /// * `buffer_description`: In case `true` is returned this buffer is filled with a description /// of the datasource (i.e. Driver name). /// /// Use [`Environment::data_source_buffer_len`] to determine buffer lengths. pub fn data_source_buffer_fill( &mut self, direction: FetchOrientation, buffer_name: &mut Vec<u16>, buffer_description: &mut Vec<u16>, ) -> Result<bool, Error> { // Use full capacity buffer_name.resize(buffer_name.capacity(), 0); buffer_description.resize(buffer_description.capacity(), 0); unsafe { match SQLDataSourcesW( self.handle, direction, buffer_name.as_mut_ptr(), buffer_name.len().try_into().unwrap(), null_mut(), buffer_description.as_mut_ptr(), buffer_description.len().try_into().unwrap(), null_mut(), ) { SqlReturn::NO_DATA => Ok(false), other => { other.into_result(self)?; Ok(true) } } } } }