kontrak_sqlite/
connection.rs

1use ffi;
2use libc::{c_char, c_int, c_void};
3use std::marker::PhantomData;
4use std::path::Path;
5
6use {Result, Statement};
7
8/// A database connection.
9pub struct Connection {
10    raw: *mut ffi::sqlite3,
11    busy_callback: Option<Box<FnMut(usize) -> bool>>,
12    phantom: PhantomData<ffi::sqlite3>,
13}
14
15unsafe impl Send for Connection {}
16
17impl Connection {
18    /// Open a connection to a new or existing database.
19    pub fn open<T: AsRef<Path>>(path: T) -> Result<Connection> {
20        let mut raw = 0 as *mut _;
21        unsafe {
22            ok!(ffi::sqlite3_open_v2(
23                path_to_cstr!(path.as_ref()).as_ptr(),
24                &mut raw,
25                ffi::SQLITE_OPEN_CREATE | ffi::SQLITE_OPEN_READWRITE,
26                0 as *const _,
27            ));
28        }
29        Ok(Connection {
30            raw: raw,
31            busy_callback: None,
32            phantom: PhantomData,
33        })
34    }
35
36    /// Execute a statement without processing the resulting rows if any.
37    #[inline]
38    pub fn execute<T: AsRef<str>>(&self, statement: T) -> Result<()> {
39        unsafe {
40            ok!(
41                self.raw,
42                ffi::sqlite3_exec(
43                    self.raw,
44                    str_to_cstr!(statement.as_ref()).as_ptr(),
45                    None,
46                    0 as *mut _,
47                    0 as *mut _,
48                )
49            );
50        }
51        Ok(())
52    }
53
54    /// Execute a statement and process the resulting rows as plain text.
55    ///
56    /// The callback is triggered for each row. If the callback returns `false`,
57    /// no more rows will be processed. For large queries and non-string data
58    /// types, prepared statement are highly preferable; see `prepare`.
59    #[inline]
60    pub fn iterate<T: AsRef<str>, F>(&self, statement: T, callback: F) -> Result<()>
61    where
62        F: FnMut(&[(&str, Option<&str>)]) -> bool,
63    {
64        unsafe {
65            let callback = Box::new(callback);
66            ok!(
67                self.raw,
68                ffi::sqlite3_exec(
69                    self.raw,
70                    str_to_cstr!(statement.as_ref()).as_ptr(),
71                    Some(process_callback::<F>),
72                    &*callback as *const F as *mut F as *mut _,
73                    0 as *mut _,
74                )
75            );
76        }
77        Ok(())
78    }
79
80    /// Create a prepared statement.
81    #[inline]
82    pub fn prepare<'l, T: AsRef<str>>(&'l self, statement: T) -> Result<Statement<'l>> {
83        ::statement::new(self.raw, statement)
84    }
85
86    /// Set a callback for handling busy events.
87    ///
88    /// The callback is triggered when the database cannot perform an operation
89    /// due to processing of some other request. If the callback returns `true`,
90    /// the operation will be repeated.
91    pub fn set_busy_handler<F>(&mut self, callback: F) -> Result<()>
92    where
93        F: FnMut(usize) -> bool + Send + 'static,
94    {
95        try!(self.remove_busy_handler());
96        unsafe {
97            let callback = Box::new(callback);
98            let result = ffi::sqlite3_busy_handler(
99                self.raw,
100                Some(busy_callback::<F>),
101                &*callback as *const F as *mut F as *mut _,
102            );
103            self.busy_callback = Some(callback);
104            ok!(self.raw, result);
105        }
106        Ok(())
107    }
108
109    /// Set an implicit callback for handling busy events that tries to repeat
110    /// rejected operations until a timeout expires.
111    #[inline]
112    pub fn set_busy_timeout(&mut self, milliseconds: usize) -> Result<()> {
113        unsafe {
114            ok!(
115                self.raw,
116                ffi::sqlite3_busy_timeout(self.raw, milliseconds as c_int)
117            );
118        }
119        Ok(())
120    }
121
122    /// Remove the callback handling busy events.
123    #[inline]
124    pub fn remove_busy_handler(&mut self) -> Result<()> {
125        self.busy_callback = None;
126        unsafe {
127            ok!(
128                self.raw,
129                ffi::sqlite3_busy_handler(self.raw, None, 0 as *mut _)
130            );
131        }
132        Ok(())
133    }
134
135    /// Return the raw pointer.
136    #[inline]
137    pub fn as_raw(&self) -> *mut ffi::sqlite3 {
138        self.raw
139    }
140}
141
142impl Drop for Connection {
143    #[inline]
144    #[allow(unused_must_use)]
145    fn drop(&mut self) {
146        self.remove_busy_handler();
147        unsafe { ffi::sqlite3_close(self.raw) };
148    }
149}
150
151extern "C" fn busy_callback<F>(callback: *mut c_void, attempts: c_int) -> c_int
152where
153    F: FnMut(usize) -> bool,
154{
155    unsafe {
156        if (*(callback as *mut F))(attempts as usize) {
157            1
158        } else {
159            0
160        }
161    }
162}
163
164extern "C" fn process_callback<F>(
165    callback: *mut c_void,
166    count: c_int,
167    values: *mut *mut c_char,
168    columns: *mut *mut c_char,
169) -> c_int
170where
171    F: FnMut(&[(&str, Option<&str>)]) -> bool,
172{
173    unsafe {
174        let mut pairs = Vec::with_capacity(count as usize);
175
176        for i in 0..(count as isize) {
177            let column = {
178                let pointer = *columns.offset(i);
179                debug_assert!(!pointer.is_null());
180                c_str_to_str!(pointer).unwrap()
181            };
182            let value = {
183                let pointer = *values.offset(i);
184                if pointer.is_null() {
185                    None
186                } else {
187                    Some(c_str_to_str!(pointer).unwrap())
188                }
189            };
190            pairs.push((column, value));
191        }
192
193        if (*(callback as *mut F))(&pairs) {
194            0
195        } else {
196            1
197        }
198    }
199}