rs_odbc/
handle.rs

1use core::{any::type_name, cell::Cell, marker::PhantomData, mem::ManuallyDrop, ptr::NonNull};
2
3#[double]
4use crate::api::ffi;
5use crate::api::{Allocate, Diagnostics, Handle};
6use crate::conn::{ConnState, C2, C3, C4};
7use crate::convert::{AsSQLHANDLE, IntoSQLPOINTER};
8use crate::desc::{AppDesc, IPD, IRD};
9use crate::env::{OdbcVersion, SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC3_80};
10#[cfg(feature = "odbc_debug")]
11use crate::stmt::{
12    SQL_ATTR_APP_PARAM_DESC, SQL_ATTR_APP_ROW_DESC, SQL_ATTR_IMP_PARAM_DESC, SQL_ATTR_IMP_ROW_DESC,
13};
14use crate::{sqlreturn::SQL_SUCCESS, Ident, SQLPOINTER};
15use mockall_double::double;
16
17/// Environment handle id
18#[derive(rs_odbc_derive::Ident)]
19#[identifier(SQLSMALLINT, 1)]
20#[allow(non_camel_case_types)]
21pub struct SQL_HANDLE_ENV;
22
23/// Connection handle id
24#[derive(rs_odbc_derive::Ident)]
25#[identifier(SQLSMALLINT, 2)]
26#[allow(non_camel_case_types)]
27pub struct SQL_HANDLE_DBC;
28
29/// Statement handle id
30#[derive(rs_odbc_derive::Ident)]
31#[identifier(SQLSMALLINT, 3)]
32#[allow(non_camel_case_types)]
33pub struct SQL_HANDLE_STMT;
34
35/// Descriptor handle id
36#[derive(rs_odbc_derive::Ident)]
37#[identifier(SQLSMALLINT, 4)]
38#[allow(non_camel_case_types)]
39pub struct SQL_HANDLE_DESC;
40
41// TODO: Check https://github.com/microsoft/ODBC-Specification/blob/b7ef71fba508ed010cd979428efae3091b732d75/Windows/inc/sqltypes.h
42// Try placing it into src/api/ffi.rs?
43// This is unixOBDC value
44pub type SQLHWND = SQLPOINTER;
45
46// TODO: But must it not be a void* in the end? It is void* in unixODBC
47// TODO: Check https://github.com/microsoft/ODBC-Specification/blob/b7ef71fba508ed010cd979428efae3091b732d75/Windows/inc/sqltypes.h
48#[repr(C)]
49//#[cfg(feature = "RUSTC_IS_STABLE")]
50pub struct RawHandle {
51    _private: [u8; 0],
52}
53//#[cfg(feature = "RUSTC_IS_NIGHTLY")]
54//pub extern type RawHandle;
55
56// TODO: Think about making it newtype with private field
57// This type must not be public ever because of the issues around Drop
58#[allow(non_camel_case_types)]
59pub type SQLHANDLE = *mut RawHandle;
60
61#[allow(non_camel_case_types)]
62pub struct SQL_NULL_HANDLE;
63
64/// An environment is a global context which holds information such as:
65/// * The environment's state
66/// * The current environment-level diagnostics
67/// * The handles of connections currently allocated on the environment
68/// * The current settings of each environment attribute
69///
70/// Environment handle is always used in calls to SQLDataSources and SQLDrivers and
71/// sometimes in calls to SQLAllocHandle, SQLEndTran, SQLFreeHandle, SQLGetDiagField, and
72/// SQLGetDiagRec.
73///
74/// # Documentation
75/// https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/environment-handles
76#[derive(Debug)]
77#[repr(transparent)]
78pub struct SQLHENV<V: OdbcVersion = SQL_OV_ODBC3_80> {
79    pub(crate) handle: SQLHANDLE,
80    version: PhantomData<V>,
81}
82
83impl<V: OdbcVersion> Handle for SQLHENV<V> {
84    type Ident = SQL_HANDLE_ENV;
85}
86
87impl<V: OdbcVersion> Allocate<'_, SQL_NULL_HANDLE> for SQLHENV<V> {
88    unsafe fn from_raw(handle: NonNull<RawHandle>) -> Self {
89        let val = Self {
90            handle: handle.as_ptr(),
91            version: PhantomData,
92        };
93
94        let sql_return = ffi::SQLSetEnvAttr(
95            val.as_SQLHANDLE(),
96            SQL_ATTR_ODBC_VERSION::IDENTIFIER,
97            V::IDENTIFIER.into_SQLPOINTER(),
98            0, // TODO: Use AttrLen::len()
99        );
100
101        if sql_return != SQL_SUCCESS {
102            panic!(
103                "SQL_ATTR_ODBC_VERSION({}): SQLSetEnvAttr returned {:?}",
104                type_name::<V>(),
105                sql_return
106            )
107        }
108
109        val
110    }
111}
112
113impl<V: OdbcVersion> Diagnostics for SQLHENV<V> {}
114
115unsafe impl<V: OdbcVersion> Send for SQLHENV<V> {}
116unsafe impl<V: OdbcVersion> Sync for SQLHENV<V> {}
117
118impl<V: OdbcVersion> Drop for SQLHENV<V> {
119    fn drop(&mut self) {
120        drop_handle(self);
121    }
122}
123
124/// Connection handle identifies a structure that contains connection information, such as the following:
125/// * The state of the connection
126/// * The current connection-level diagnostics
127/// * The handles of statements and descriptors currently allocated on the connection
128/// * The current settings of each connection attribute
129///
130/// Connection handle is used when:
131/// * Connecting to the data source (SQLConnect, SQLDriverConnect, or SQLBrowseConnect)
132/// * Disconnecting from the data source (SQLDisconnect)
133/// * Getting information about the driver and data source (SQLGetInfo)
134/// * Retrieving diagnostics (SQLGetDiagField and SQLGetDiagRec) * Performing transactions (SQLEndTran)
135/// * Setting and getting connection attributes (SQLSetConnectAttr and SQLGetConnectAttr)
136/// * Getting the native format of an SQL statement (SQLNativeSql)
137///
138/// Connection handles are allocated with SQLAllocHandle and freed with SQLFreeHandle.
139///
140/// # Documentation
141/// https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/connection-handles
142#[derive(Debug)]
143#[repr(transparent)]
144pub struct SQLHDBC<'env, C: ConnState, V: OdbcVersion = SQL_OV_ODBC3_80> {
145    pub(crate) handle: SQLHANDLE,
146
147    parent: PhantomData<&'env ()>,
148    connected: PhantomData<C>,
149    version: PhantomData<V>,
150}
151
152impl<C: ConnState, V: OdbcVersion> Handle for SQLHDBC<'_, C, V> {
153    type Ident = SQL_HANDLE_DBC;
154}
155
156impl<'env, V: OdbcVersion> Allocate<'env, SQLHENV<V>> for SQLHDBC<'env, C2, V> {
157    unsafe fn from_raw(handle: NonNull<RawHandle>) -> Self {
158        Self {
159            handle: handle.as_ptr(),
160
161            parent: PhantomData,
162            connected: PhantomData,
163            version: PhantomData,
164        }
165    }
166}
167
168impl<C: ConnState, V: OdbcVersion> Diagnostics for SQLHDBC<'_, C, V> {}
169
170unsafe impl<C: ConnState, V: OdbcVersion> Send for SQLHDBC<'_, C, V> {}
171// TODO: Reference: http://www.firstsql.com/ithread5.htm
172//  Connection Options (set with SQLSetConnectOption) should be set before sharing begins and should not be changed.
173//  Connection-level Statement Options (set with SQLSetConnectOption) should be set before sharing begins and should not be changed.
174//  Transactions, there are several choices:
175//      autocommit, each statement is implicitly committed,
176//      connection-wide, a single transaction during the entire connection,
177//      otherwise, commits and rollbacks must be synchronized between threads.
178// unsafe impl<V: OdbcVersion> Sync for SQLHDBC<'_, C4, V> {}
179
180impl<C: ConnState, V: OdbcVersion> Drop for SQLHDBC<'_, C, V> {
181    fn drop(&mut self) {
182        C::disconnect(self);
183        drop_handle(self);
184    }
185}
186
187impl<'env, OC: ConnState, V: OdbcVersion> SQLHDBC<'env, OC, V> {
188    pub(crate) fn disconnect(self) -> SQLHDBC<'env, C2, V> {
189        let handle = ManuallyDrop::new(self);
190
191        SQLHDBC {
192            handle: handle.handle,
193            parent: handle.parent,
194            connected: PhantomData,
195            version: PhantomData,
196        }
197    }
198    pub(crate) fn need_data(self) -> SQLHDBC<'env, C3, V> {
199        let handle = ManuallyDrop::new(self);
200
201        SQLHDBC {
202            handle: handle.handle,
203            parent: handle.parent,
204            connected: PhantomData,
205            version: PhantomData,
206        }
207    }
208    pub(crate) fn connect(self) -> SQLHDBC<'env, C4, V> {
209        let handle = ManuallyDrop::new(self);
210
211        SQLHDBC {
212            handle: handle.handle,
213            parent: handle.parent,
214            connected: PhantomData,
215            version: PhantomData,
216        }
217    }
218}
219
220/// Statement handle consists of all of the information associated with a SQL statement,
221/// such as any result sets created by the statement and parameters used in the execution
222/// of the statement. A statement is associated with a single connection, and there can be
223/// multiple statements on that connection. The statement handle contains statement
224/// information, such as:
225/// * The statement's state
226/// * The current statement-level diagnostics
227/// * The addresses of the application variables bound to the statement's parameters and result set columns
228/// * The current settings of each statement attribute
229///
230/// Statement handles are used in most ODBC functions. Notably, they are used:
231/// * to bind parameters and result set columns (SQLBindParameter and SQLBindCol)
232/// * to prepare and execute statements (SQLPrepare, SQLExecute, and SQLExecDirect)
233/// * to retrieve metadata (SQLColAttribute and SQLDescribeCol)
234/// * to fetch results (SQLFetch), and retrieve diagnostics (SQLGetDiagField and SQLGetDiagRec)
235/// * in catalog functions (SQLColumns, SQLTables, ...)
236/// * in number of other functions.
237///
238/// Statement handles are allocated with SQLAllocHandle and freed with SQLFreeHandle.
239///
240/// # Documentation
241/// https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/statement-handles
242#[derive(Debug)]
243#[repr(transparent)]
244pub struct SQLHSTMT<'conn, 'desc, 'buf, V: OdbcVersion = SQL_OV_ODBC3_80>(
245    pub(crate) UnsafeSQLHSTMT<'conn, 'desc, 'buf, V>,
246);
247
248impl<'conn, 'desc, 'buf, V: OdbcVersion> Handle for SQLHSTMT<'conn, 'desc, 'buf, V> {
249    type Ident = <UnsafeSQLHSTMT<'conn, 'desc, 'buf, V> as Handle>::Ident;
250}
251
252#[allow(non_snake_case)]
253impl<'env, 'conn, V: OdbcVersion> Allocate<'conn, SQLHDBC<'env, C4, V>>
254    for SQLHSTMT<'conn, '_, '_, V>
255{
256    unsafe fn from_raw(handle: NonNull<RawHandle>) -> Self {
257        Self(UnsafeSQLHSTMT::from_raw(handle))
258    }
259}
260
261impl<V: OdbcVersion> Diagnostics for SQLHSTMT<'_, '_, '_, V> {}
262
263unsafe impl<V: OdbcVersion> Send for SQLHSTMT<'_, '_, '_, V> {}
264
265/// SQLHSTMT which allows for the use of ODBC API whose safety cannot be determined by the type system (e.g. SQL_DESC_BIND_OFFSET_PTR)
266///
267#[derive(Debug)]
268#[cfg_attr(not(feature = "odbc_debug"), repr(transparent))]
269pub struct UnsafeSQLHSTMT<'conn, 'desc, 'buf, V: OdbcVersion = SQL_OV_ODBC3_80> {
270    pub(crate) handle: SQLHANDLE,
271
272    parent: PhantomData<&'conn ()>,
273    version: PhantomData<V>,
274
275    #[cfg(feature = "odbc_debug")]
276    pub(crate) explicit_ard: Cell<Option<&'desc UnsafeSQLHDESC<'desc, AppDesc<'buf>, V>>>,
277    #[cfg(feature = "odbc_debug")]
278    pub(crate) explicit_apd: Cell<Option<&'desc UnsafeSQLHDESC<'desc, AppDesc<'buf>, V>>>,
279
280    #[cfg(feature = "odbc_debug")]
281    pub(crate) ard: ManuallyDrop<UnsafeSQLHDESC<'desc, AppDesc<'buf>, V>>,
282    #[cfg(feature = "odbc_debug")]
283    pub(crate) apd: ManuallyDrop<UnsafeSQLHDESC<'desc, AppDesc<'buf>, V>>,
284    #[cfg(feature = "odbc_debug")]
285    pub(crate) ird: ManuallyDrop<UnsafeSQLHDESC<'desc, IRD, V>>,
286    #[cfg(feature = "odbc_debug")]
287    pub(crate) ipd: ManuallyDrop<UnsafeSQLHDESC<'desc, IPD, V>>,
288
289    #[cfg(not(feature = "odbc_debug"))]
290    pub(crate) explicit_ard: Cell<PhantomData<&'desc UnsafeSQLHDESC<'desc, AppDesc<'buf>, V>>>,
291    #[cfg(not(feature = "odbc_debug"))]
292    pub(crate) explicit_apd: Cell<PhantomData<&'desc UnsafeSQLHDESC<'desc, AppDesc<'buf>, V>>>,
293
294    #[cfg(not(feature = "odbc_debug"))]
295    pub(crate) ard: PhantomData<UnsafeSQLHDESC<'desc, AppDesc<'buf>, V>>,
296    #[cfg(not(feature = "odbc_debug"))]
297    pub(crate) apd: PhantomData<UnsafeSQLHDESC<'desc, AppDesc<'buf>, V>>,
298    #[cfg(not(feature = "odbc_debug"))]
299    pub(crate) ird: PhantomData<UnsafeSQLHDESC<'desc, IRD, V>>,
300    #[cfg(not(feature = "odbc_debug"))]
301    pub(crate) ipd: PhantomData<UnsafeSQLHDESC<'desc, IPD, V>>,
302}
303
304impl<V: OdbcVersion> Handle for UnsafeSQLHSTMT<'_, '_, '_, V> {
305    type Ident = SQL_HANDLE_STMT;
306}
307
308impl<'env, 'conn, V: OdbcVersion> Allocate<'conn, SQLHDBC<'env, C4, V>>
309    for UnsafeSQLHSTMT<'conn, '_, '_, V>
310{
311    #[cfg(feature = "odbc_debug")]
312    unsafe fn from_raw(handle: NonNull<RawHandle>) -> Self {
313        unsafe {
314            let ard = UnsafeSQLHSTMT::<V>::get_descriptor_handle::<SQL_ATTR_APP_ROW_DESC>(handle);
315            let apd = UnsafeSQLHSTMT::<V>::get_descriptor_handle::<SQL_ATTR_APP_PARAM_DESC>(handle);
316            let ird = UnsafeSQLHSTMT::<V>::get_descriptor_handle::<SQL_ATTR_IMP_ROW_DESC>(handle);
317            let ipd = UnsafeSQLHSTMT::<V>::get_descriptor_handle::<SQL_ATTR_IMP_PARAM_DESC>(handle);
318
319            Self {
320                parent: PhantomData,
321                version: PhantomData,
322
323                handle,
324
325                ard: ManuallyDrop::new(UnsafeSQLHDESC::from_raw(ard)),
326                apd: ManuallyDrop::new(UnsafeSQLHDESC::from_raw(apd)),
327                ird: ManuallyDrop::new(UnsafeSQLHDESC::from_raw(ird)),
328                ipd: ManuallyDrop::new(UnsafeSQLHDESC::from_raw(ipd)),
329
330                explicit_ard: Cell::new(None),
331                explicit_apd: Cell::new(None),
332            }
333        }
334    }
335
336    #[cfg(not(feature = "odbc_debug"))]
337    unsafe fn from_raw(handle: NonNull<RawHandle>) -> Self {
338        Self {
339            handle: handle.as_ptr(),
340
341            parent: PhantomData,
342            version: PhantomData,
343
344            ard: PhantomData,
345            apd: PhantomData,
346            ird: PhantomData,
347            ipd: PhantomData,
348
349            explicit_ard: Cell::new(PhantomData),
350            explicit_apd: Cell::new(PhantomData),
351        }
352    }
353}
354
355impl<V: OdbcVersion> Diagnostics for UnsafeSQLHSTMT<'_, '_, '_, V> {}
356
357impl<'buf, V: OdbcVersion> UnsafeSQLHSTMT<'_, '_, 'buf, V> {
358    #[cfg(feature = "odbc_debug")]
359    unsafe fn get_descriptor_handle<A: Ident<Type = SQLINTEGER>>(handle: SQLHANDLE) -> SQLHANDLE {
360        let mut descriptor_handle = MaybeUninit::uninit();
361
362        let sql_return = ffi::SQLGetStmtAttrA(
363            handle,
364            A::IDENTIFIER,
365            descriptor_handle.as_mut_ptr() as SQLPOINTER,
366            0,
367            &mut 0,
368        );
369        if sql_return != SQL_SUCCESS {
370            panic!(
371                "{}: SQLGetStmtAttr returned {:?}",
372                type_name::<A>(),
373                sql_return
374            );
375        }
376
377        descriptor_handle.assume_init()
378    }
379}
380
381unsafe impl<V: OdbcVersion> Send for UnsafeSQLHSTMT<'_, '_, '_, V> {}
382
383impl<V: OdbcVersion> Drop for UnsafeSQLHSTMT<'_, '_, '_, V> {
384    fn drop(&mut self) {
385        drop_handle(self);
386    }
387}
388
389/// A descriptor is a collection of metadata that describes the parameters of an SQL
390/// statement or the columns of a result set. Thus, a descriptor can fill four roles:
391/// * (APD)Application Parameter Descriptor:
392///     Contains information about the application buffers bound to the parameters in an
393///     SQL statement, such as their addresses, lengths, and C data types.
394/// * (IPD)Implementation Parameter Descriptor:
395///     Contains information about the parameters in an SQL statement, such as their SQL
396///     data types, lengths, and nullability.
397/// * (ARD)Application Row Descriptor:
398///     Contains information about the application buffers bound to the columns in a
399///     result set, such as their addresses, lengths, and C data types.
400/// * (IRD)Implementation Row Descriptor:
401///     Contains information about the columns in a result set, such as their SQL data
402///     types, lengths, and nullability.
403///
404/// Four descriptors are allocated automatically when a statement is allocated, but
405/// applications can also allocate descriptors with SQLAllocHandle. They are allocated on
406/// a connection and can be associated with one or more statements on that connection to
407/// fulfill the role of an APD or ARD on those statements.
408///
409/// # Documentation
410/// https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/descriptor-handles
411#[derive(Debug)]
412#[repr(transparent)]
413pub struct SQLHDESC<'conn, DT, V: OdbcVersion = SQL_OV_ODBC3_80>(
414    pub(crate) UnsafeSQLHDESC<'conn, DT, V>,
415);
416
417impl<DT, V: OdbcVersion> Handle for SQLHDESC<'_, DT, V> {
418    type Ident = SQL_HANDLE_DESC;
419}
420
421#[allow(non_snake_case)]
422impl<'env, 'conn, 'buf, V: OdbcVersion> Allocate<'conn, SQLHDBC<'env, C4, V>>
423    for SQLHDESC<'conn, AppDesc<'buf>, V>
424{
425    unsafe fn from_raw(handle: NonNull<RawHandle>) -> Self {
426        Self(UnsafeSQLHDESC::from_raw(handle))
427    }
428}
429
430impl<DT, V: OdbcVersion> Diagnostics for SQLHDESC<'_, DT, V> {}
431
432unsafe impl<DT, V: OdbcVersion> Send for SQLHDESC<'_, DT, V> {}
433
434/// SQLHDESC which allows for the use of ODBC API whose safety cannot be determined by the type system (e.g. SQL_DESC_BIND_OFFSET_PTR)
435///
436#[derive(Debug)]
437#[cfg_attr(not(feature = "odbc_debug"), repr(transparent))]
438pub struct UnsafeSQLHDESC<'conn, T, V: OdbcVersion = SQL_OV_ODBC3_80> {
439    pub(crate) handle: SQLHANDLE,
440
441    parent: PhantomData<&'conn ()>,
442    version: PhantomData<V>,
443
444    #[cfg(feature = "odbc_debug")]
445    // TODO: Implement properly
446    pub(crate) inner: PhantomData<T>,
447    #[cfg(not(feature = "odbc_debug"))]
448    pub(crate) inner: PhantomData<T>,
449}
450
451impl<V: OdbcVersion, T> Handle for UnsafeSQLHDESC<'_, T, V> {
452    type Ident = SQL_HANDLE_DESC;
453}
454
455// Valid because SQLHDBC is covariant
456impl<'env, 'conn, 'buf, V: OdbcVersion> Allocate<'conn, SQLHDBC<'env, C4, V>>
457    for UnsafeSQLHDESC<'conn, AppDesc<'buf>, V>
458{
459    unsafe fn from_raw(handle: NonNull<RawHandle>) -> Self {
460        Self {
461            handle: handle.as_ptr(),
462
463            parent: PhantomData,
464            version: PhantomData,
465
466            inner: PhantomData,
467        }
468    }
469}
470
471impl<DT, V: OdbcVersion> Diagnostics for UnsafeSQLHDESC<'_, DT, V> {}
472
473unsafe impl<DT, V: OdbcVersion> Send for UnsafeSQLHDESC<'_, DT, V> {}
474
475impl<V: OdbcVersion, DT> Drop for UnsafeSQLHDESC<'_, DT, V> {
476    fn drop(&mut self) {
477        drop_handle(self);
478    }
479}
480
481#[derive(Debug)]
482#[repr(transparent)]
483pub struct RefUnsafeSQLHDESC<'conn, DT, V: OdbcVersion = SQL_OV_ODBC3_80>(
484    ManuallyDrop<UnsafeSQLHDESC<'conn, DT, V>>,
485);
486unsafe impl<DT, V: OdbcVersion> AsSQLHANDLE for RefUnsafeSQLHDESC<'_, DT, V> {
487    fn as_SQLHANDLE(&self) -> SQLHANDLE {
488        self.0.as_SQLHANDLE()
489    }
490}
491impl<V: OdbcVersion, DT> Handle for RefUnsafeSQLHDESC<'_, DT, V> {
492    type Ident = SQL_HANDLE_DESC;
493}
494
495impl<DT, V: OdbcVersion> Diagnostics for RefUnsafeSQLHDESC<'_, DT, V> {}
496
497#[derive(Debug)]
498#[repr(transparent)]
499pub struct RefSQLHDESC<'conn, DT, V: OdbcVersion = SQL_OV_ODBC3_80>(
500    RefUnsafeSQLHDESC<'conn, DT, V>,
501);
502unsafe impl<'conn, DT, V: OdbcVersion> AsSQLHANDLE for RefSQLHDESC<'conn, DT, V> {
503    fn as_SQLHANDLE(&self) -> SQLHANDLE {
504        self.0.as_SQLHANDLE()
505    }
506}
507impl<V: OdbcVersion, DT> Handle for RefSQLHDESC<'_, DT, V> {
508    type Ident = SQL_HANDLE_DESC;
509}
510
511impl<DT, V: OdbcVersion> Diagnostics for RefSQLHDESC<'_, DT, V> {}
512
513fn drop_handle<H: Handle>(handle: &mut H) {
514    let sql_return = unsafe { ffi::SQLFreeHandle(H::Ident::IDENTIFIER, handle.as_SQLHANDLE()) };
515
516    #[cfg(feature = "std")]
517    if std::thread::panicking() {
518        return;
519    }
520
521    if sql_return != SQL_SUCCESS {
522        panic!(
523            "{}: SQLFreeHandle returned: {:?}",
524            type_name::<H>(),
525            sql_return
526        )
527    }
528}
529
530#[cfg(test)]
531mod test {
532    #![allow(non_snake_case)]
533
534    use super::*;
535
536    #[test]
537    fn env_SQL_OV_ODBC3_80_version_set() {
538        let env_raw_handle = NonNull::new(13 as SQLHANDLE).unwrap();
539
540        let SQLSetEnvAttr_ctx = ffi::SQLSetEnvAttr_context();
541        let SQLFreeHandle_ctx = ffi::SQLFreeHandle_context();
542
543        SQLSetEnvAttr_ctx
544            .expect()
545            .once()
546            .withf_st(move |x, y, z, w| {
547                *x == env_raw_handle.as_ptr()
548                    && *y == SQL_ATTR_ODBC_VERSION::IDENTIFIER
549                    && *z == SQL_OV_ODBC3_80::IDENTIFIER.into_SQLPOINTER()
550                    && *w == 0
551            })
552            .return_const(SQL_SUCCESS);
553        SQLFreeHandle_ctx
554            .expect()
555            .once()
556            .withf_st(move |x, y| *x == SQL_HANDLE_ENV::IDENTIFIER && *y == env_raw_handle.as_ptr())
557            .return_const(SQL_SUCCESS);
558
559        unsafe { SQLHENV::<SQL_OV_ODBC3_80>::from_raw(env_raw_handle) };
560    }
561
562    #[test]
563    fn disconnect_C2() {
564        let conn_raw_handle = 13 as SQLHANDLE;
565
566        let SQLDisconnect_ctx = ffi::SQLDisconnect_context();
567        let SQLFreeHandle_ctx = ffi::SQLFreeHandle_context();
568
569        SQLDisconnect_ctx.expect().never();
570        SQLFreeHandle_ctx
571            .expect()
572            .once()
573            .withf_st(move |x, y| *x == SQL_HANDLE_DBC::IDENTIFIER && *y == conn_raw_handle)
574            .return_const(SQL_SUCCESS);
575
576        SQLHDBC::<C2, SQL_OV_ODBC3_80> {
577            handle: conn_raw_handle,
578            parent: PhantomData,
579            connected: PhantomData,
580            version: PhantomData,
581        };
582    }
583
584    #[test]
585    fn disconnect_C3() {
586        let conn_raw_handle = 13 as SQLHANDLE;
587
588        let SQLDisconnect_ctx = ffi::SQLDisconnect_context();
589        let SQLFreeHandle_ctx = ffi::SQLFreeHandle_context();
590
591        SQLDisconnect_ctx
592            .expect()
593            .once()
594            .withf_st(move |x| *x == conn_raw_handle)
595            .return_const(SQL_SUCCESS);
596        SQLFreeHandle_ctx
597            .expect()
598            .once()
599            .withf_st(move |x, y| *x == SQL_HANDLE_DBC::IDENTIFIER && *y == conn_raw_handle)
600            .return_const(SQL_SUCCESS);
601
602        SQLHDBC::<C3, SQL_OV_ODBC3_80> {
603            handle: conn_raw_handle,
604            parent: PhantomData,
605            connected: PhantomData,
606            version: PhantomData,
607        };
608    }
609
610    #[test]
611    fn disconnect_C4() {
612        let conn_raw_handle = 13 as SQLHANDLE;
613
614        let SQLDisconnect_ctx = ffi::SQLDisconnect_context();
615        let SQLFreeHandle_ctx = ffi::SQLFreeHandle_context();
616
617        SQLDisconnect_ctx
618            .expect()
619            .once()
620            .withf_st(move |x| *x == conn_raw_handle)
621            .return_const(SQL_SUCCESS);
622        SQLFreeHandle_ctx
623            .expect()
624            .once()
625            .withf_st(move |x, y| *x == SQL_HANDLE_DBC::IDENTIFIER && *y == conn_raw_handle)
626            .return_const(SQL_SUCCESS);
627
628        SQLHDBC::<C4, SQL_OV_ODBC3_80> {
629            handle: conn_raw_handle,
630            parent: PhantomData,
631            connected: PhantomData,
632            version: PhantomData,
633        };
634    }
635
636    // TODO: Mockall is buggy and these tests fail more often
637    //#[test]
638    //#[should_panic]
639    //fn disconnect_C3_panic() {
640    //    let conn_raw_handle = 13 as SQLHANDLE;
641
642    //    let SQLDisconnect_ctx = ffi::SQLDisconnect_context();
643    //    let SQLFreeHandle_ctx = ffi::SQLFreeHandle_context();
644
645    //    SQLDisconnect_ctx
646    //        .expect()
647    //        .once()
648    //        .withf_st(move |x| *x == conn_raw_handle)
649    //        .return_const(SQL_ERROR);
650    //    SQLFreeHandle_ctx
651    //        .expect()
652    //        .once()
653    //        .withf_st(move |x, y| *x == SQL_HANDLE_DBC::IDENTIFIER && *y == conn_raw_handle)
654    //        .return_const(SQL_SUCCESS);
655
656    //    SQLHDBC::<C3, SQL_OV_ODBC3_80> {
657    //        handle: conn_raw_handle,
658    //        parent: PhantomData,
659    //        connected: PhantomData,
660    //        version: PhantomData
661    //    };
662    //}
663
664    //#[test]
665    //#[should_panic]
666    //fn disconnect_C4_panic() {
667    //    let conn_raw_handle = 13 as SQLHANDLE;
668
669    //    let SQLDisconnect_ctx = ffi::SQLDisconnect_context();
670    //    let SQLFreeHandle_ctx = ffi::SQLFreeHandle_context();
671
672    //    SQLDisconnect_ctx
673    //        .expect()
674    //        .once()
675    //        .withf_st(move |x| *x == conn_raw_handle)
676    //        .return_const(SQL_ERROR);
677    //    SQLFreeHandle_ctx
678    //        .expect()
679    //        .once()
680    //        .withf_st(move |x, y| *x == SQL_HANDLE_DBC::IDENTIFIER && *y == conn_raw_handle)
681    //        .return_const(SQL_SUCCESS);
682
683    //    SQLHDBC::<C3, SQL_OV_ODBC3_80> {
684    //        handle: conn_raw_handle,
685    //        parent: PhantomData,
686    //        connected: PhantomData,
687    //        version: PhantomData
688    //    };
689    //}
690}