serde_odbc/
connection.rs

1/*
2This file is part of serde-odbc.
3
4serde-odbc is free software: you can redistribute it and/or modify
5it under the terms of the GNU Lesser General Public License as published by
6the Free Software Foundation, either version 3 of the License, or
7(at your option) any later version.
8
9serde-odbc is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12GNU Lesser General Public License for more details.
13
14You should have received a copy of the GNU Lesser General Public License
15along with serde-odbc.  If not, see <http://www.gnu.org/licenses/>.
16*/
17use 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}