1use std::ptr::null_mut;
18
19use odbc_sys::{
20 SQLAllocHandle, SQLDriverConnect, SQLEndTran, SQLFreeHandle, SQLSetConnectAttr, SQLSetEnvAttr,
21 SqlCompletionType, SQLHANDLE, SQLHDBC, SQLHENV, SQLSMALLINT, SQL_ATTR_AUTOCOMMIT,
22 SQL_ATTR_CONNECTION_POOLING, SQL_ATTR_ODBC_VERSION, SQL_COMMIT, SQL_DRIVER_COMPLETE_REQUIRED,
23 SQL_HANDLE_DBC, SQL_HANDLE_ENV, SQL_OV_ODBC3, SQL_ROLLBACK,
24};
25
26use crate::error::{OdbcResult, Result};
27
28pub struct Environment(SQLHENV);
29
30impl Environment {
31 pub fn new() -> Result<Self> {
32 let mut env: SQLHANDLE = null_mut();
33
34 unsafe { SQLAllocHandle(SQL_HANDLE_ENV, null_mut(), &mut env) }.check()?;
35
36 let env = env as SQLHENV;
37
38 unsafe { SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC3.into(), 0) }.check()?;
39 unsafe { SQLSetEnvAttr(env, SQL_ATTR_CONNECTION_POOLING, null_mut(), 0) }.check()?;
40
41 Ok(Environment(env))
42 }
43
44 pub fn handle(&self) -> SQLHANDLE {
45 self.0 as SQLHANDLE
46 }
47}
48
49impl Drop for Environment {
50 fn drop(&mut self) {
51 let _ = unsafe { SQLFreeHandle(SQL_HANDLE_ENV, self.handle()) };
52 }
53}
54
55pub struct Connection(SQLHDBC);
56
57impl Connection {
58 pub fn new(env: &Environment, conn_str: &str) -> Result<Self> {
59 let mut dbc: SQLHANDLE = null_mut();
60
61 unsafe { SQLAllocHandle(SQL_HANDLE_DBC, env.handle(), &mut dbc) }.check()?;
62
63 let dbc = dbc as SQLHDBC;
64
65 unsafe {
66 SQLDriverConnect(
67 dbc,
68 null_mut(),
69 conn_str.as_ptr(),
70 conn_str.len() as SQLSMALLINT,
71 null_mut(),
72 0,
73 null_mut(),
74 SQL_DRIVER_COMPLETE_REQUIRED,
75 )
76 }
77 .check()?;
78
79 unsafe { SQLSetConnectAttr(dbc, SQL_ATTR_AUTOCOMMIT, null_mut(), 0) }.check()?;
80
81 Ok(Connection(dbc))
82 }
83
84 pub fn handle(&self) -> SQLHANDLE {
85 self.0 as SQLHANDLE
86 }
87
88 pub fn begin(&self) -> Transaction {
89 Transaction(Some(self))
90 }
91}
92
93impl Drop for Connection {
94 fn drop(&mut self) {
95 let _ = unsafe { SQLFreeHandle(SQL_HANDLE_DBC, self.handle()) };
96 }
97}
98
99pub struct Transaction<'conn>(Option<&'conn Connection>);
100
101impl<'conn> Transaction<'conn> {
102 pub fn commit(mut self) -> Result<()> {
103 Self::end(self.0.take().unwrap(), SQL_COMMIT)
104 }
105
106 pub fn rollback(mut self) -> Result<()> {
107 Self::end(self.0.take().unwrap(), SQL_ROLLBACK)
108 }
109
110 fn end(conn: &'conn Connection, completion_type: SqlCompletionType) -> Result<()> {
111 unsafe { SQLEndTran(SQL_HANDLE_DBC, conn.handle(), completion_type) }.check()
112 }
113}
114
115impl<'conn> Drop for Transaction<'conn> {
116 fn drop(&mut self) {
117 if let Some(conn) = self.0.take() {
118 let _ = Self::end(conn, SQL_ROLLBACK);
119 }
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 use crate::tests::CONN_STR;
128
129 #[test]
130 fn make_env() {
131 Environment::new().unwrap();
132 }
133
134 #[test]
135 fn make_conn() {
136 let env = Environment::new().unwrap();
137 Connection::new(&env, CONN_STR).unwrap();
138 }
139
140 #[test]
141 fn commit_trans() {
142 let env = Environment::new().unwrap();
143 let conn = Connection::new(&env, CONN_STR).unwrap();
144
145 let trans = conn.begin();
146 trans.commit().unwrap();
147 }
148}