sqlite_bindings_lunatic/
connection.rs

1use ffi;
2use libc::{c_char, c_int, c_void};
3use std::marker::PhantomData;
4use std::ops::{Deref, DerefMut};
5use std::path::Path;
6
7use error::Result;
8use statement::Statement;
9
10use crate::Error;
11
12/// A database connection.
13pub struct Connection {
14    raw: Raw,
15    busy_callback: Option<Box<dyn FnMut(usize) -> bool + Send>>,
16    phantom: PhantomData<ffi::sqlite3>,
17}
18
19/// A thread-safe database connection.
20pub struct ConnectionWithFullMutex(Connection);
21
22/// Flags for opening a database connection.
23#[derive(Clone, Copy, Debug)]
24pub struct OpenFlags(c_int);
25
26struct Raw(*mut ffi::sqlite3);
27
28impl Connection {
29    /// Open a read-write connection to a new or existing database.
30    pub fn open<T: AsRef<Path>>(path: T) -> Result<Connection> {
31        Connection::open_with_flags(path, OpenFlags::new().set_create().set_read_write())
32    }
33
34    /// Open a connection with specific flags.
35    pub fn open_with_flags<T: AsRef<Path>>(path: T, flags: OpenFlags) -> Result<Connection> {
36        let mut raw = 0 as *mut _;
37        unsafe {
38            let code = ffi::sqlite3_open_v2(
39                path_to_cstr!(path.as_ref()).as_ptr(),
40                &mut raw,
41                flags.0,
42                0 as *const _,
43            );
44            match code {
45                ffi::SQLITE_OK => {}
46                code => match ::error::last(raw) {
47                    Some(error) => {
48                        ffi::sqlite3_close(raw);
49                        return Err(error);
50                    }
51                    _ => {
52                        ffi::sqlite3_close(raw);
53                        return Err(::Error {
54                            code: Some(code as isize),
55                            message: None,
56                        });
57                    }
58                },
59            }
60        }
61        Ok(Connection {
62            raw: Raw(raw),
63            busy_callback: None,
64            phantom: PhantomData,
65        })
66    }
67
68    /// Open a thread-safe read-write connection to a new or existing database.
69    pub fn open_with_full_mutex<T: AsRef<Path>>(path: T) -> Result<ConnectionWithFullMutex> {
70        Connection::open_with_flags(
71            path,
72            OpenFlags::new()
73                .set_create()
74                .set_read_write()
75                .set_full_mutex(),
76        )
77        .map(ConnectionWithFullMutex)
78    }
79
80    /// Execute a statement without processing the resulting rows if any.
81    #[inline]
82    pub fn execute<T: AsRef<str>>(&self, statement: T) -> Result<()> {
83        unsafe {
84            ok!(
85                self.raw.0,
86                ffi::sqlite3_exec(
87                    self.raw.0,
88                    str_to_cstr!(statement.as_ref()).as_ptr(),
89                    None,
90                    0 as *mut _,
91                    0 as *mut _,
92                )
93            );
94        }
95        Ok(())
96    }
97
98    /// Execute a statement and process the resulting rows as plain text.
99    ///
100    /// The callback is triggered for each row. If the callback returns `false`,
101    /// no more rows will be processed. For large queries and non-string data
102    /// types, prepared statement are highly preferable; see `prepare`.
103    #[inline]
104    pub fn iterate<T: AsRef<str>, F>(&self, statement: T, callback: F) -> Result<()>
105    where
106        F: FnMut(&[(&str, Option<&str>)]) -> bool,
107    {
108        unsafe {
109            let callback = Box::new(callback);
110            ok!(
111                self.raw.0,
112                ffi::sqlite3_exec(
113                    self.raw.0,
114                    str_to_cstr!(statement.as_ref()).as_ptr(),
115                    Some(process_callback::<F>),
116                    &*callback as *const F as *mut F as *mut _,
117                    0 as *mut _,
118                )
119            );
120        }
121        Ok(())
122    }
123
124    /// Create a prepared statement.
125    #[inline]
126    pub fn prepare<'l, T: AsRef<str>>(&'l self, statement: T) -> Result<Statement> {
127        ::statement::new(self.raw.0, statement)
128    }
129
130    /// Return the number of rows inserted, updated, or deleted by the most
131    /// recent INSERT, UPDATE, or DELETE statement.
132    #[inline]
133    pub fn change_count(&self) -> usize {
134        unsafe { ffi::sqlite3_changes(self.raw.0) as usize }
135    }
136
137    /// Return the total number of rows inserted, updated, and deleted by all
138    /// INSERT, UPDATE, and DELETE statements since the connection was opened.
139    #[inline]
140    pub fn total_change_count(&self) -> usize {
141        unsafe { ffi::sqlite3_total_changes(self.raw.0) as usize }
142    }
143
144    /// Set a callback for handling busy events.
145    ///
146    /// The callback is triggered when the database cannot perform an operation
147    /// due to processing of some other request. If the callback returns `true`,
148    /// the operation will be repeated.
149    pub fn set_busy_handler<F>(&mut self, callback: F) -> Result<()>
150    where
151        F: FnMut(usize) -> bool + Send + 'static,
152    {
153        self.remove_busy_handler()?;
154        unsafe {
155            let callback = Box::new(callback);
156            let result = ffi::sqlite3_busy_handler(
157                self.raw.0,
158                Some(busy_callback::<F>),
159                &*callback as *const F as *mut F as *mut _,
160            );
161            self.busy_callback = Some(callback);
162            ok!(self.raw.0, result);
163        }
164        Ok(())
165    }
166
167    /// Set an implicit callback for handling busy events that tries to repeat
168    /// rejected operations until a timeout expires.
169    #[inline]
170    pub fn set_busy_timeout(&mut self, milliseconds: usize) -> Result<()> {
171        unsafe {
172            ok!(
173                self.raw.0,
174                ffi::sqlite3_busy_timeout(self.raw.0, milliseconds as c_int)
175            );
176        }
177        Ok(())
178    }
179
180    /// Remove the callback handling busy events.
181    #[inline]
182    pub fn remove_busy_handler(&mut self) -> Result<()> {
183        self.busy_callback = None;
184        unsafe {
185            ok!(
186                self.raw.0,
187                ffi::sqlite3_busy_handler(self.raw.0, None, 0 as *mut _)
188            );
189        }
190        Ok(())
191    }
192
193    #[doc(hidden)]
194    #[inline]
195    pub fn as_raw(&self) -> *mut ffi::sqlite3 {
196        self.raw.0
197    }
198
199    /// return last error in connection
200    pub fn last(&mut self) -> Option<Error> {
201        let raw = self.as_raw();
202        unsafe {
203            let code = ffi::sqlite3_errcode(raw);
204            if code == ffi::SQLITE_OK {
205                return None;
206            }
207            let message = ffi::sqlite3_errmsg(raw);
208            if message.is_null() {
209                return None;
210            }
211            Some(Error {
212                code: Some(code as isize),
213                message: Some(c_str_to_string!(message)),
214            })
215        }
216    }
217}
218
219impl Drop for Connection {
220    #[inline]
221    #[allow(unused_must_use)]
222    fn drop(&mut self) {
223        self.remove_busy_handler();
224        unsafe { ffi::sqlite3_close(self.raw.0) };
225    }
226}
227
228impl OpenFlags {
229    /// Create flags for opening a database connection.
230    #[inline]
231    pub fn new() -> Self {
232        OpenFlags(0)
233    }
234
235    /// Create the database if it does not already exist.
236    pub fn set_create(mut self) -> Self {
237        self.0 |= ffi::SQLITE_OPEN_CREATE;
238        self
239    }
240
241    /// Open the database in the serialized [threading mode][1].
242    ///
243    /// [1]: https://www.sqlite.org/threadsafe.html
244    pub fn set_full_mutex(mut self) -> Self {
245        self.0 |= ffi::SQLITE_OPEN_FULLMUTEX;
246        self
247    }
248
249    /// Opens the database in the multi-thread [threading mode][1].
250    ///
251    /// [1]: https://www.sqlite.org/threadsafe.html
252    pub fn set_no_mutex(mut self) -> Self {
253        self.0 |= ffi::SQLITE_OPEN_NOMUTEX;
254        self
255    }
256
257    /// Open the database for reading only.
258    pub fn set_read_only(mut self) -> Self {
259        self.0 |= ffi::SQLITE_OPEN_READONLY;
260        self
261    }
262
263    /// Open the database for reading and writing.
264    pub fn set_read_write(mut self) -> Self {
265        self.0 |= ffi::SQLITE_OPEN_READWRITE;
266        self
267    }
268}
269
270impl Deref for ConnectionWithFullMutex {
271    type Target = Connection;
272
273    #[inline]
274    fn deref(&self) -> &Self::Target {
275        &self.0
276    }
277}
278
279impl DerefMut for ConnectionWithFullMutex {
280    #[inline]
281    fn deref_mut(&mut self) -> &mut Self::Target {
282        &mut self.0
283    }
284}
285
286unsafe impl Sync for ConnectionWithFullMutex {}
287
288unsafe impl Send for Raw {}
289
290extern "C" fn busy_callback<F>(callback: *mut c_void, attempts: c_int) -> c_int
291where
292    F: FnMut(usize) -> bool,
293{
294    unsafe {
295        if (*(callback as *mut F))(attempts as usize) {
296            1
297        } else {
298            0
299        }
300    }
301}
302
303extern "C" fn process_callback<F>(
304    callback: *mut c_void,
305    count: c_int,
306    values: *mut *mut c_char,
307    columns: *mut *mut c_char,
308) -> c_int
309where
310    F: FnMut(&[(&str, Option<&str>)]) -> bool,
311{
312    unsafe {
313        let mut pairs = Vec::with_capacity(count as usize);
314        for index in 0..(count as isize) {
315            let column = {
316                let pointer = *columns.offset(index);
317                debug_assert!(!pointer.is_null());
318                c_str_to_str!(pointer).unwrap()
319            };
320            let value = {
321                let pointer = *values.offset(index);
322                if pointer.is_null() {
323                    None
324                } else {
325                    Some(c_str_to_str!(pointer).unwrap())
326                }
327            };
328            pairs.push((column, value));
329        }
330        if (*(callback as *mut F))(&pairs) {
331            0
332        } else {
333            1
334        }
335    }
336}