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 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
use super::{
as_handle::AsHandle,
drop_handle,
sql_char::SqlChar,
sql_result::{ExtSqlReturn, SqlResult},
Connection,
};
use odbc_sys::{
AttrCpMatch, AttrOdbcVersion, EnvironmentAttribute, FetchOrientation, HDbc, HEnv, Handle,
HandleType, SQLAllocHandle, SQLSetEnvAttr,
};
use std::ptr::null_mut;
#[cfg(feature = "narrow")]
use odbc_sys::{SQLDataSources as sql_data_sources, SQLDrivers as sql_drivers};
#[cfg(not(feature = "narrow"))]
use odbc_sys::{SQLDataSourcesW as sql_data_sources, SQLDriversW as sql_drivers};
/// 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 {}
// We are not declaring Environment as Sync due to its interior mutablity with regards to iterator
// state and error handilng
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 {
/// Enable or disable (default) connection pooling for ODBC connections. Call this function
/// before creating the ODBC environment for which you want to enable connection pooling.
///
/// See:
/// <https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/driver-manager-connection-pooling>
///
/// # Safety
///
/// > An ODBC driver must be fully thread-safe, and connections must not have thread affinity to
/// > support connection pooling. This means the driver is able to handle a call on any thread
/// > at any time and is able to connect on one thread, to use the connection on another thread,
/// > and to disconnect on a third thread.
pub unsafe fn set_connection_pooling(scheme: odbc_sys::AttrConnectionPooling) -> SqlResult<()> {
SQLSetEnvAttr(
null_mut(),
odbc_sys::EnvironmentAttribute::ConnectionPooling,
scheme.into(),
0,
)
.into_sql_result("SQLSetEnvAttr")
}
pub fn set_connection_pooling_matching(&mut self, matching: AttrCpMatch) -> SqlResult<()> {
unsafe {
SQLSetEnvAttr(
self.handle,
odbc_sys::EnvironmentAttribute::CpMatch,
matching.into(),
0,
)
}
.into_sql_result("SQLSetEnvAttr")
}
/// An allocated ODBC Environment handle
pub fn new() -> SqlResult<Self> {
// After running a lot of unit tests in parallel on both linux and windows architectures and
// never seeing a race condition related to this I deem this save. In the past I feared
// concurrent construction of multiple Environments might race on shared state. Mostly due
// to <https://github.com/Koka/odbc-rs/issues/29> and
// <http://old.vk.pp.ru/docs/sybase-any/interfaces/00000034.htm>. Since however I since
// however official sources imply it is ok for an application to have multiple environments
// and I did not get it to race ever on my machine.
unsafe {
let mut handle = null_mut();
let result: SqlResult<()> = SQLAllocHandle(HandleType::Env, null_mut(), &mut handle)
.into_sql_result("SQLAllocHandle");
result.on_success(|| Environment {
handle: handle as HEnv,
})
}
}
/// 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) -> SqlResult<()> {
unsafe {
SQLSetEnvAttr(
self.handle,
EnvironmentAttribute::OdbcVersion,
version.into(),
0,
)
.into_sql_result("SQLSetEnvAttr")
}
}
/// Allocate a new connection handle. The `Connection` must not outlive the `Environment`.
pub fn allocate_connection(&self) -> SqlResult<Connection<'_>> {
let mut handle = null_mut();
unsafe {
SQLAllocHandle(HandleType::Dbc, self.as_handle(), &mut handle)
.into_sql_result("SQLAllocHandle")
.on_success(|| 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. Returns `None` to indicate the end
/// of the list.
///
/// # Safety
///
/// Callers need to make sure only one thread is iterating over driver information at a time.
/// Method changes environment state. This method would be safe to call via an exclusive `&mut`
/// reference, yet that would restrict usecases. E.g. requesting information would only be
/// possible before connections borrow a reference.
///
/// # 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 unsafe fn drivers_buffer_fill(
&self,
direction: FetchOrientation,
buffer_description: &mut [SqlChar],
buffer_attributes: &mut [SqlChar],
) -> Option<SqlResult<()>> {
sql_drivers(
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(),
)
.into_opt_sql_result("SQLDrivers")
}
/// Use together with [`Environment::drivers_buffer_fill`] to list drivers descriptions and
/// driver attribute keywords.
///
/// # Safety
///
/// Callers need to make sure only one thread is iterating over driver information at a time.
/// Method changes environment state. This method would be safe to call via an exclusive `&mut`
/// reference, yet that would restrict usecases. E.g. requesting information would only be
/// possible before connections borrow a reference.
///
/// # 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 unsafe fn drivers_buffer_len(
&self,
direction: FetchOrientation,
) -> Option<SqlResult<(i16, i16)>> {
// Lengths in characters minus terminating zero
let mut length_description: i16 = 0;
let mut length_attributes: i16 = 0;
// Determine required buffer size
sql_drivers(
self.handle,
direction,
null_mut(),
0,
&mut length_description,
null_mut(),
0,
&mut length_attributes,
)
.into_opt_sql_result("SQLDrivers")
.map(|res| res.on_success(|| (length_description, length_attributes)))
}
/// Use together with [`Environment::data_source_buffer_fill`] to list drivers descriptions and
/// driver attribute keywords.
///
/// # Safety
///
/// Callers need to make sure only one thread is iterating over data source information at a
/// time. Method changes environment state. This method would be safe to call via an exclusive
/// `&mut` reference, yet that would restrict usecases. E.g. requesting information would only
/// be possible before connections borrow a reference.
///
/// # 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 unsafe fn data_source_buffer_len(
&self,
direction: FetchOrientation,
) -> Option<SqlResult<(i16, i16)>> {
// Lengths in characters minus terminating zero
let mut length_name: i16 = 0;
let mut length_description: i16 = 0;
// Determine required buffer size
sql_data_sources(
self.handle,
direction,
null_mut(),
0,
&mut length_name,
null_mut(),
0,
&mut length_description,
)
.into_opt_sql_result("SQLDataSources")
.map(|res| res.on_success(|| (length_name, length_description)))
}
/// List drivers descriptions and driver attribute keywords.
///
/// # Safety
///
/// Callers need to make sure only one thread is iterating over data source information at a
/// time. Method changes environment state. This method would be safe to call via an exclusive
/// `&mut` reference, yet that would restrict usecases. E.g. requesting information would only
/// be possible before connections borrow a reference.
///
/// # 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 unsafe fn data_source_buffer_fill(
&self,
direction: FetchOrientation,
buffer_name: &mut [SqlChar],
buffer_description: &mut [SqlChar],
) -> Option<SqlResult<()>> {
sql_data_sources(
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(),
)
.into_opt_sql_result("SQLDataSources")
}
}