use core::{any::type_name, cell::Cell, marker::PhantomData, mem::ManuallyDrop, ptr::NonNull};
#[double]
use crate::api::ffi;
use crate::api::{Allocate, Diagnostics, Handle};
use crate::conn::{ConnState, C2, C3, C4};
use crate::convert::{AsSQLHANDLE, IntoSQLPOINTER};
use crate::desc::{AppDesc, IPD, IRD};
use crate::env::{OdbcVersion, SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC3_80};
#[cfg(feature = "odbc_debug")]
use crate::stmt::{
SQL_ATTR_APP_PARAM_DESC, SQL_ATTR_APP_ROW_DESC, SQL_ATTR_IMP_PARAM_DESC, SQL_ATTR_IMP_ROW_DESC,
};
use crate::{sqlreturn::SQL_SUCCESS, Ident, SQLPOINTER};
use mockall_double::double;
#[derive(rs_odbc_derive::Ident)]
#[identifier(SQLSMALLINT, 1)]
#[allow(non_camel_case_types)]
pub struct SQL_HANDLE_ENV;
#[derive(rs_odbc_derive::Ident)]
#[identifier(SQLSMALLINT, 2)]
#[allow(non_camel_case_types)]
pub struct SQL_HANDLE_DBC;
#[derive(rs_odbc_derive::Ident)]
#[identifier(SQLSMALLINT, 3)]
#[allow(non_camel_case_types)]
pub struct SQL_HANDLE_STMT;
#[derive(rs_odbc_derive::Ident)]
#[identifier(SQLSMALLINT, 4)]
#[allow(non_camel_case_types)]
pub struct SQL_HANDLE_DESC;
pub type SQLHWND = SQLPOINTER;
#[repr(C)]
pub struct RawHandle {
_private: [u8; 0],
}
#[allow(non_camel_case_types)]
pub type SQLHANDLE = *mut RawHandle;
#[allow(non_camel_case_types)]
pub struct SQL_NULL_HANDLE;
#[derive(Debug)]
#[repr(transparent)]
pub struct SQLHENV<V: OdbcVersion = SQL_OV_ODBC3_80> {
pub(crate) handle: SQLHANDLE,
version: PhantomData<V>,
}
impl<V: OdbcVersion> Handle for SQLHENV<V> {
type Ident = SQL_HANDLE_ENV;
}
impl<V: OdbcVersion> Allocate<'_, SQL_NULL_HANDLE> for SQLHENV<V> {
unsafe fn from_raw(handle: NonNull<RawHandle>) -> Self {
let val = Self {
handle: handle.as_ptr(),
version: PhantomData,
};
let sql_return = ffi::SQLSetEnvAttr(
val.as_SQLHANDLE(),
SQL_ATTR_ODBC_VERSION::IDENTIFIER,
V::IDENTIFIER.into_SQLPOINTER(),
0, );
if sql_return != SQL_SUCCESS {
panic!(
"SQL_ATTR_ODBC_VERSION({}): SQLSetEnvAttr returned {:?}",
type_name::<V>(),
sql_return
)
}
val
}
}
impl<V: OdbcVersion> Diagnostics for SQLHENV<V> {}
unsafe impl<V: OdbcVersion> Send for SQLHENV<V> {}
unsafe impl<V: OdbcVersion> Sync for SQLHENV<V> {}
impl<V: OdbcVersion> Drop for SQLHENV<V> {
fn drop(&mut self) {
drop_handle(self);
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct SQLHDBC<'env, C: ConnState, V: OdbcVersion = SQL_OV_ODBC3_80> {
pub(crate) handle: SQLHANDLE,
parent: PhantomData<&'env ()>,
connected: PhantomData<C>,
version: PhantomData<V>,
}
impl<C: ConnState, V: OdbcVersion> Handle for SQLHDBC<'_, C, V> {
type Ident = SQL_HANDLE_DBC;
}
impl<'env, V: OdbcVersion> Allocate<'env, SQLHENV<V>> for SQLHDBC<'env, C2, V> {
unsafe fn from_raw(handle: NonNull<RawHandle>) -> Self {
Self {
handle: handle.as_ptr(),
parent: PhantomData,
connected: PhantomData,
version: PhantomData,
}
}
}
impl<C: ConnState, V: OdbcVersion> Diagnostics for SQLHDBC<'_, C, V> {}
unsafe impl<C: ConnState, V: OdbcVersion> Send for SQLHDBC<'_, C, V> {}
impl<C: ConnState, V: OdbcVersion> Drop for SQLHDBC<'_, C, V> {
fn drop(&mut self) {
C::disconnect(self);
drop_handle(self);
}
}
impl<'env, OC: ConnState, V: OdbcVersion> SQLHDBC<'env, OC, V> {
pub(crate) fn disconnect(self) -> SQLHDBC<'env, C2, V> {
let handle = ManuallyDrop::new(self);
SQLHDBC {
handle: handle.handle,
parent: handle.parent,
connected: PhantomData,
version: PhantomData,
}
}
pub(crate) fn need_data(self) -> SQLHDBC<'env, C3, V> {
let handle = ManuallyDrop::new(self);
SQLHDBC {
handle: handle.handle,
parent: handle.parent,
connected: PhantomData,
version: PhantomData,
}
}
pub(crate) fn connect(self) -> SQLHDBC<'env, C4, V> {
let handle = ManuallyDrop::new(self);
SQLHDBC {
handle: handle.handle,
parent: handle.parent,
connected: PhantomData,
version: PhantomData,
}
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct SQLHSTMT<'conn, 'desc, 'buf, V: OdbcVersion = SQL_OV_ODBC3_80>(
pub(crate) UnsafeSQLHSTMT<'conn, 'desc, 'buf, V>,
);
impl<'conn, 'desc, 'buf, V: OdbcVersion> Handle for SQLHSTMT<'conn, 'desc, 'buf, V> {
type Ident = <UnsafeSQLHSTMT<'conn, 'desc, 'buf, V> as Handle>::Ident;
}
#[allow(non_snake_case)]
impl<'env, 'conn, V: OdbcVersion> Allocate<'conn, SQLHDBC<'env, C4, V>>
for SQLHSTMT<'conn, '_, '_, V>
{
unsafe fn from_raw(handle: NonNull<RawHandle>) -> Self {
Self(UnsafeSQLHSTMT::from_raw(handle))
}
}
impl<V: OdbcVersion> Diagnostics for SQLHSTMT<'_, '_, '_, V> {}
unsafe impl<V: OdbcVersion> Send for SQLHSTMT<'_, '_, '_, V> {}
#[derive(Debug)]
#[cfg_attr(not(feature = "odbc_debug"), repr(transparent))]
pub struct UnsafeSQLHSTMT<'conn, 'desc, 'buf, V: OdbcVersion = SQL_OV_ODBC3_80> {
pub(crate) handle: SQLHANDLE,
parent: PhantomData<&'conn ()>,
version: PhantomData<V>,
#[cfg(feature = "odbc_debug")]
pub(crate) explicit_ard: Cell<Option<&'desc UnsafeSQLHDESC<'desc, AppDesc<'buf>, V>>>,
#[cfg(feature = "odbc_debug")]
pub(crate) explicit_apd: Cell<Option<&'desc UnsafeSQLHDESC<'desc, AppDesc<'buf>, V>>>,
#[cfg(feature = "odbc_debug")]
pub(crate) ard: ManuallyDrop<UnsafeSQLHDESC<'desc, AppDesc<'buf>, V>>,
#[cfg(feature = "odbc_debug")]
pub(crate) apd: ManuallyDrop<UnsafeSQLHDESC<'desc, AppDesc<'buf>, V>>,
#[cfg(feature = "odbc_debug")]
pub(crate) ird: ManuallyDrop<UnsafeSQLHDESC<'desc, IRD, V>>,
#[cfg(feature = "odbc_debug")]
pub(crate) ipd: ManuallyDrop<UnsafeSQLHDESC<'desc, IPD, V>>,
#[cfg(not(feature = "odbc_debug"))]
pub(crate) explicit_ard: Cell<PhantomData<&'desc UnsafeSQLHDESC<'desc, AppDesc<'buf>, V>>>,
#[cfg(not(feature = "odbc_debug"))]
pub(crate) explicit_apd: Cell<PhantomData<&'desc UnsafeSQLHDESC<'desc, AppDesc<'buf>, V>>>,
#[cfg(not(feature = "odbc_debug"))]
pub(crate) ard: PhantomData<UnsafeSQLHDESC<'desc, AppDesc<'buf>, V>>,
#[cfg(not(feature = "odbc_debug"))]
pub(crate) apd: PhantomData<UnsafeSQLHDESC<'desc, AppDesc<'buf>, V>>,
#[cfg(not(feature = "odbc_debug"))]
pub(crate) ird: PhantomData<UnsafeSQLHDESC<'desc, IRD, V>>,
#[cfg(not(feature = "odbc_debug"))]
pub(crate) ipd: PhantomData<UnsafeSQLHDESC<'desc, IPD, V>>,
}
impl<V: OdbcVersion> Handle for UnsafeSQLHSTMT<'_, '_, '_, V> {
type Ident = SQL_HANDLE_STMT;
}
impl<'env, 'conn, V: OdbcVersion> Allocate<'conn, SQLHDBC<'env, C4, V>>
for UnsafeSQLHSTMT<'conn, '_, '_, V>
{
#[cfg(feature = "odbc_debug")]
unsafe fn from_raw(handle: NonNull<RawHandle>) -> Self {
unsafe {
let ard = UnsafeSQLHSTMT::<V>::get_descriptor_handle::<SQL_ATTR_APP_ROW_DESC>(handle);
let apd = UnsafeSQLHSTMT::<V>::get_descriptor_handle::<SQL_ATTR_APP_PARAM_DESC>(handle);
let ird = UnsafeSQLHSTMT::<V>::get_descriptor_handle::<SQL_ATTR_IMP_ROW_DESC>(handle);
let ipd = UnsafeSQLHSTMT::<V>::get_descriptor_handle::<SQL_ATTR_IMP_PARAM_DESC>(handle);
Self {
parent: PhantomData,
version: PhantomData,
handle,
ard: ManuallyDrop::new(UnsafeSQLHDESC::from_raw(ard)),
apd: ManuallyDrop::new(UnsafeSQLHDESC::from_raw(apd)),
ird: ManuallyDrop::new(UnsafeSQLHDESC::from_raw(ird)),
ipd: ManuallyDrop::new(UnsafeSQLHDESC::from_raw(ipd)),
explicit_ard: Cell::new(None),
explicit_apd: Cell::new(None),
}
}
}
#[cfg(not(feature = "odbc_debug"))]
unsafe fn from_raw(handle: NonNull<RawHandle>) -> Self {
Self {
handle: handle.as_ptr(),
parent: PhantomData,
version: PhantomData,
ard: PhantomData,
apd: PhantomData,
ird: PhantomData,
ipd: PhantomData,
explicit_ard: Cell::new(PhantomData),
explicit_apd: Cell::new(PhantomData),
}
}
}
impl<V: OdbcVersion> Diagnostics for UnsafeSQLHSTMT<'_, '_, '_, V> {}
impl<'buf, V: OdbcVersion> UnsafeSQLHSTMT<'_, '_, 'buf, V> {
#[cfg(feature = "odbc_debug")]
unsafe fn get_descriptor_handle<A: Ident<Type = SQLINTEGER>>(handle: SQLHANDLE) -> SQLHANDLE {
let mut descriptor_handle = MaybeUninit::uninit();
let sql_return = ffi::SQLGetStmtAttrA(
handle,
A::IDENTIFIER,
descriptor_handle.as_mut_ptr() as SQLPOINTER,
0,
&mut 0,
);
if sql_return != SQL_SUCCESS {
panic!(
"{}: SQLGetStmtAttr returned {:?}",
type_name::<A>(),
sql_return
);
}
descriptor_handle.assume_init()
}
}
unsafe impl<V: OdbcVersion> Send for UnsafeSQLHSTMT<'_, '_, '_, V> {}
impl<V: OdbcVersion> Drop for UnsafeSQLHSTMT<'_, '_, '_, V> {
fn drop(&mut self) {
drop_handle(self);
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct SQLHDESC<'conn, DT, V: OdbcVersion = SQL_OV_ODBC3_80>(
pub(crate) UnsafeSQLHDESC<'conn, DT, V>,
);
impl<DT, V: OdbcVersion> Handle for SQLHDESC<'_, DT, V> {
type Ident = SQL_HANDLE_DESC;
}
#[allow(non_snake_case)]
impl<'env, 'conn, 'buf, V: OdbcVersion> Allocate<'conn, SQLHDBC<'env, C4, V>>
for SQLHDESC<'conn, AppDesc<'buf>, V>
{
unsafe fn from_raw(handle: NonNull<RawHandle>) -> Self {
Self(UnsafeSQLHDESC::from_raw(handle))
}
}
impl<DT, V: OdbcVersion> Diagnostics for SQLHDESC<'_, DT, V> {}
unsafe impl<DT, V: OdbcVersion> Send for SQLHDESC<'_, DT, V> {}
#[derive(Debug)]
#[cfg_attr(not(feature = "odbc_debug"), repr(transparent))]
pub struct UnsafeSQLHDESC<'conn, T, V: OdbcVersion = SQL_OV_ODBC3_80> {
pub(crate) handle: SQLHANDLE,
parent: PhantomData<&'conn ()>,
version: PhantomData<V>,
#[cfg(feature = "odbc_debug")]
pub(crate) inner: PhantomData<T>,
#[cfg(not(feature = "odbc_debug"))]
pub(crate) inner: PhantomData<T>,
}
impl<V: OdbcVersion, T> Handle for UnsafeSQLHDESC<'_, T, V> {
type Ident = SQL_HANDLE_DESC;
}
impl<'env, 'conn, 'buf, V: OdbcVersion> Allocate<'conn, SQLHDBC<'env, C4, V>>
for UnsafeSQLHDESC<'conn, AppDesc<'buf>, V>
{
unsafe fn from_raw(handle: NonNull<RawHandle>) -> Self {
Self {
handle: handle.as_ptr(),
parent: PhantomData,
version: PhantomData,
inner: PhantomData,
}
}
}
impl<DT, V: OdbcVersion> Diagnostics for UnsafeSQLHDESC<'_, DT, V> {}
unsafe impl<DT, V: OdbcVersion> Send for UnsafeSQLHDESC<'_, DT, V> {}
impl<V: OdbcVersion, DT> Drop for UnsafeSQLHDESC<'_, DT, V> {
fn drop(&mut self) {
drop_handle(self);
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct RefUnsafeSQLHDESC<'conn, DT, V: OdbcVersion = SQL_OV_ODBC3_80>(
ManuallyDrop<UnsafeSQLHDESC<'conn, DT, V>>,
);
unsafe impl<DT, V: OdbcVersion> AsSQLHANDLE for RefUnsafeSQLHDESC<'_, DT, V> {
fn as_SQLHANDLE(&self) -> SQLHANDLE {
self.0.as_SQLHANDLE()
}
}
impl<V: OdbcVersion, DT> Handle for RefUnsafeSQLHDESC<'_, DT, V> {
type Ident = SQL_HANDLE_DESC;
}
impl<DT, V: OdbcVersion> Diagnostics for RefUnsafeSQLHDESC<'_, DT, V> {}
#[derive(Debug)]
#[repr(transparent)]
pub struct RefSQLHDESC<'conn, DT, V: OdbcVersion = SQL_OV_ODBC3_80>(
RefUnsafeSQLHDESC<'conn, DT, V>,
);
unsafe impl<'conn, DT, V: OdbcVersion> AsSQLHANDLE for RefSQLHDESC<'conn, DT, V> {
fn as_SQLHANDLE(&self) -> SQLHANDLE {
self.0.as_SQLHANDLE()
}
}
impl<V: OdbcVersion, DT> Handle for RefSQLHDESC<'_, DT, V> {
type Ident = SQL_HANDLE_DESC;
}
impl<DT, V: OdbcVersion> Diagnostics for RefSQLHDESC<'_, DT, V> {}
fn drop_handle<H: Handle>(handle: &mut H) {
let sql_return = unsafe { ffi::SQLFreeHandle(H::Ident::IDENTIFIER, handle.as_SQLHANDLE()) };
#[cfg(feature = "std")]
if std::thread::panicking() {
return;
}
if sql_return != SQL_SUCCESS {
panic!(
"{}: SQLFreeHandle returned: {:?}",
type_name::<H>(),
sql_return
)
}
}
#[cfg(test)]
mod test {
#![allow(non_snake_case)]
use super::*;
#[test]
fn env_SQL_OV_ODBC3_80_version_set() {
let env_raw_handle = NonNull::new(13 as SQLHANDLE).unwrap();
let SQLSetEnvAttr_ctx = ffi::SQLSetEnvAttr_context();
let SQLFreeHandle_ctx = ffi::SQLFreeHandle_context();
SQLSetEnvAttr_ctx
.expect()
.once()
.withf_st(move |x, y, z, w| {
*x == env_raw_handle.as_ptr()
&& *y == SQL_ATTR_ODBC_VERSION::IDENTIFIER
&& *z == SQL_OV_ODBC3_80::IDENTIFIER.into_SQLPOINTER()
&& *w == 0
})
.return_const(SQL_SUCCESS);
SQLFreeHandle_ctx
.expect()
.once()
.withf_st(move |x, y| *x == SQL_HANDLE_ENV::IDENTIFIER && *y == env_raw_handle.as_ptr())
.return_const(SQL_SUCCESS);
unsafe { SQLHENV::<SQL_OV_ODBC3_80>::from_raw(env_raw_handle) };
}
#[test]
fn disconnect_C2() {
let conn_raw_handle = 13 as SQLHANDLE;
let SQLDisconnect_ctx = ffi::SQLDisconnect_context();
let SQLFreeHandle_ctx = ffi::SQLFreeHandle_context();
SQLDisconnect_ctx.expect().never();
SQLFreeHandle_ctx
.expect()
.once()
.withf_st(move |x, y| *x == SQL_HANDLE_DBC::IDENTIFIER && *y == conn_raw_handle)
.return_const(SQL_SUCCESS);
SQLHDBC::<C2, SQL_OV_ODBC3_80> {
handle: conn_raw_handle,
parent: PhantomData,
connected: PhantomData,
version: PhantomData,
};
}
#[test]
fn disconnect_C3() {
let conn_raw_handle = 13 as SQLHANDLE;
let SQLDisconnect_ctx = ffi::SQLDisconnect_context();
let SQLFreeHandle_ctx = ffi::SQLFreeHandle_context();
SQLDisconnect_ctx
.expect()
.once()
.withf_st(move |x| *x == conn_raw_handle)
.return_const(SQL_SUCCESS);
SQLFreeHandle_ctx
.expect()
.once()
.withf_st(move |x, y| *x == SQL_HANDLE_DBC::IDENTIFIER && *y == conn_raw_handle)
.return_const(SQL_SUCCESS);
SQLHDBC::<C3, SQL_OV_ODBC3_80> {
handle: conn_raw_handle,
parent: PhantomData,
connected: PhantomData,
version: PhantomData,
};
}
#[test]
fn disconnect_C4() {
let conn_raw_handle = 13 as SQLHANDLE;
let SQLDisconnect_ctx = ffi::SQLDisconnect_context();
let SQLFreeHandle_ctx = ffi::SQLFreeHandle_context();
SQLDisconnect_ctx
.expect()
.once()
.withf_st(move |x| *x == conn_raw_handle)
.return_const(SQL_SUCCESS);
SQLFreeHandle_ctx
.expect()
.once()
.withf_st(move |x, y| *x == SQL_HANDLE_DBC::IDENTIFIER && *y == conn_raw_handle)
.return_const(SQL_SUCCESS);
SQLHDBC::<C4, SQL_OV_ODBC3_80> {
handle: conn_raw_handle,
parent: PhantomData,
connected: PhantomData,
version: PhantomData,
};
}
}