fce_sqlite_connector/connection.rs
1use sqlite3_connector as ffi;
2
3use std::marker::PhantomData;
4use std::path::Path;
5
6use {Result, Statement};
7
8/// A database connection.
9pub struct Connection {
10 raw: ffi::Sqlite3DbHandle,
11 phantom: PhantomData<ffi::Sqlite3DbHandle>,
12}
13
14/// Flags for opening a database connection.
15#[derive(Clone, Copy, Debug)]
16pub struct OpenFlags(i32);
17
18unsafe impl Send for Connection {}
19
20impl Connection {
21 /// Open a read-write connection to a new or existing database.
22 pub fn open<T: AsRef<Path>>(path: T) -> Result<Connection> {
23 Connection::open_with_flags(path, OpenFlags::new().set_create().set_read_write())
24 }
25
26 /// Open a database connection with specific flags.
27 pub fn open_with_flags<T: AsRef<Path>>(path: T, flags: OpenFlags) -> Result<Connection> {
28 unsafe {
29 let path = path.as_ref();
30 let path = path.to_string_lossy().into_owned();
31 let result = ffi::sqlite3_open_v2(path, flags.0, String::new());
32
33 match result.ret_code {
34 ffi::SQLITE_OK => {}
35 code => {
36 return match ::last_error(result.db_handle) {
37 Some(error) => {
38 ffi::sqlite3_close(result.db_handle);
39 Err(error)
40 }
41 _ => {
42 ffi::sqlite3_close(result.db_handle);
43 Err(::Error {
44 code: Some(code as isize),
45 message: None,
46 })
47 }
48 }
49 }
50 }
51
52 Ok(Connection {
53 raw: result.db_handle,
54 phantom: PhantomData,
55 })
56 }
57 }
58
59 /// Execute a statement without processing the resulting rows if any.
60 #[inline]
61 pub fn execute<T: AsRef<str>>(&self, statement: T) -> Result<()> {
62 unsafe {
63 ok_descr!(
64 self.raw,
65 ffi::sqlite3_exec(self.raw, statement.as_ref().into(), 0, 0,)
66 );
67 }
68 Ok(())
69 }
70
71 /// Execute a statement and process the resulting rows as plain text.
72 ///
73 /// The callback is triggered for each row. If the callback returns `false`,
74 /// no more rows will be processed. For large queries and non-string data
75 /// types, prepared statement are highly preferable; see `prepare`.
76 #[inline]
77 pub fn iterate<T: AsRef<str>, F>(&self, statement: T, callback: F) -> Result<()>
78 where
79 F: FnMut(&[(&str, Option<&str>)]) -> bool,
80 {
81 unsafe {
82 let _callback = Box::new(callback);
83 ok_descr!(
84 self.raw,
85 ffi::sqlite3_exec(self.raw, statement.as_ref().into(), 0, 0,)
86 );
87 }
88 Ok(())
89 }
90
91 /// Create a prepared statement.
92 #[inline]
93 pub fn prepare<T: AsRef<str>>(&self, statement: T) -> Result<Statement> {
94 ::statement::new(self.raw, statement)
95 }
96
97 /// Return the number of rows inserted, updated, or deleted by the most
98 /// recent INSERT, UPDATE, or DELETE statement.
99 #[inline]
100 pub fn changes(&self) -> usize {
101 unsafe { ffi::sqlite3_changes(self.raw) as usize }
102 }
103
104 /// Return the total number of rows inserted, updated, and deleted by all
105 /// INSERT, UPDATE, and DELETE statements since the connection was opened.
106 #[inline]
107 pub fn total_changes(&self) -> usize {
108 unsafe { ffi::sqlite3_total_changes(self.raw) as usize }
109 }
110
111 /// Set an implicit callback for handling busy events that tries to repeat
112 /// rejected operations until a timeout expires.
113 #[inline]
114 pub fn set_busy_timeout(&mut self, milliseconds: usize) -> Result<()> {
115 unsafe {
116 ok_raw!(
117 self.raw,
118 ffi::sqlite3_busy_timeout(self.raw, milliseconds as _)
119 );
120 }
121 Ok(())
122 }
123
124 /// Return the raw pointer.
125 #[inline]
126 pub fn as_raw(&self) -> ffi::Sqlite3DbHandle {
127 self.raw
128 }
129}
130
131impl Drop for Connection {
132 #[inline]
133 #[allow(unused_must_use)]
134 fn drop(&mut self) {
135 unsafe { ffi::sqlite3_close(self.raw) };
136 }
137}
138
139impl OpenFlags {
140 /// Create flags for opening a database connection.
141 #[inline]
142 pub fn new() -> Self {
143 OpenFlags(0)
144 }
145
146 /// Create the database if it does not already exist.
147 pub fn set_create(mut self) -> Self {
148 self.0 |= ffi::SQLITE_OPEN_CREATE;
149 self
150 }
151
152 /// Open the database in the serialized [threading mode][1].
153 ///
154 /// [1]: https://www.sqlite.org/threadsafe.html
155 pub fn set_full_mutex(mut self) -> Self {
156 self.0 |= ffi::SQLITE_OPEN_FULLMUTEX;
157 self
158 }
159
160 /// Opens the database in the multi-thread [threading mode][1].
161 ///
162 /// [1]: https://www.sqlite.org/threadsafe.html
163 pub fn set_no_mutex(mut self) -> Self {
164 self.0 |= ffi::SQLITE_OPEN_NOMUTEX;
165 self
166 }
167
168 /// Open the database for reading only.
169 pub fn set_read_only(mut self) -> Self {
170 self.0 |= ffi::SQLITE_OPEN_READONLY;
171 self
172 }
173
174 /// Open the database for reading and writing.
175 pub fn set_read_write(mut self) -> Self {
176 self.0 |= ffi::SQLITE_OPEN_READWRITE;
177 self
178 }
179}
180
181/*
182extern "C" fn process_callback<F>(
183 callback: *mut c_void,
184 count: i32,
185 values: *mut *mut c_char,
186 columns: *mut *mut c_char,
187) -> i32
188where
189 F: FnMut(&[(&str, Option<&str>)]) -> bool,
190{
191 unsafe {
192 let mut pairs = Vec::with_capacity(count as usize);
193 for i in 0..(count as isize) {
194 let column = {
195 let pointer = *columns.offset(i);
196 debug_assert!(!pointer.is_null());
197 c_str_to_str!(pointer).unwrap()
198 };
199 let value = {
200 let pointer = *values.offset(i);
201 if pointer.is_null() {
202 None
203 } else {
204 Some(c_str_to_str!(pointer).unwrap())
205 }
206 };
207 pairs.push((column, value));
208 }
209 if (*(callback as *mut F))(&pairs) {
210 0
211 } else {
212 1
213 }
214 }
215}
216*/