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#[derive(rs_odbc_derive::Ident)]
19#[identifier(SQLSMALLINT, 1)]
20#[allow(non_camel_case_types)]
21pub struct SQL_HANDLE_ENV;
22
23#[derive(rs_odbc_derive::Ident)]
25#[identifier(SQLSMALLINT, 2)]
26#[allow(non_camel_case_types)]
27pub struct SQL_HANDLE_DBC;
28
29#[derive(rs_odbc_derive::Ident)]
31#[identifier(SQLSMALLINT, 3)]
32#[allow(non_camel_case_types)]
33pub struct SQL_HANDLE_STMT;
34
35#[derive(rs_odbc_derive::Ident)]
37#[identifier(SQLSMALLINT, 4)]
38#[allow(non_camel_case_types)]
39pub struct SQL_HANDLE_DESC;
40
41pub type SQLHWND = SQLPOINTER;
45
46#[repr(C)]
49pub struct RawHandle {
51 _private: [u8; 0],
52}
53#[allow(non_camel_case_types)]
59pub type SQLHANDLE = *mut RawHandle;
60
61#[allow(non_camel_case_types)]
62pub struct SQL_NULL_HANDLE;
63
64#[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, );
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#[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> {}
171impl<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#[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#[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#[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#[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 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
455impl<'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 }