1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
#[cfg(modern_sqlite)]
use crate::mutex::SQLiteMutexGuard;
use crate::{ffi, sqlite3_match_version, sqlite3_require_version, types::*};
use bitflags::bitflags;
#[cfg(modern_sqlite)]
use std::ptr::{null, NonNull};
use std::{
ffi::{CStr, CString},
mem::MaybeUninit,
ops::{Deref, DerefMut},
os::raw::c_int,
path::Path,
ptr::null_mut,
thread::panicking,
};
bitflags! {
/// These are the flags that can be passed to [Database::open_with_flags] and variants.
#[repr(transparent)]
pub struct OpenFlags: c_int {
/// The database is opened in read-only mode. If the database does not already exist, an error is returned.
const READONLY = ffi::SQLITE_OPEN_READONLY;
/// The database is opened for reading and writing if possible, or reading only
/// if the file is write protected by the operating system. In either case the
/// database must already exist, otherwise an error is returned (see
/// [Self::CREATE]).
const READWRITE = ffi::SQLITE_OPEN_READWRITE;
/// Create a new, empty database file when opening if it does not already
/// exist. This only applies to [Self::READWRITE].
const CREATE = ffi::SQLITE_OPEN_CREATE;
/// The database will be opened as an in-memory database. The database is named
/// by the "filename" argument for the purposes of cache-sharing, if shared
/// cache mode is enabled, but the "filename" is otherwise ignored.
const MEMORY = ffi::SQLITE_OPEN_MEMORY;
/// The new database connection will use the "multi-thread" threading mode.
/// This means that separate threads are allowed to use SQLite at the same
/// time, as long as each thread is using a different database connection.
///
/// # Safety
///
/// When using this flag, you assume responsibility for verifying that the
/// database connection will only be accessed from a single thread.
const UNSAFE_NOMUTEX = ffi::SQLITE_OPEN_NOMUTEX;
/// The new database connection will use the "serialized" threading mode. This
/// means the multiple threads can safely attempt to use the same database
/// connection at the same time. (Mutexes will block any actual concurrency,
/// but in this mode there is no harm in trying.)
const FULLMUTEX = ffi::SQLITE_OPEN_FULLMUTEX;
/// The database is opened shared cache enabled, overriding the default shared
/// cache setting provided by [ffi::sqlite3_enable_shared_cache].
const SHAREDCACHE = ffi::SQLITE_OPEN_SHAREDCACHE;
/// The database is opened shared cache disabled, overriding the default shared
/// cache setting provided by [ffi::sqlite3_enable_shared_cache].
const PRIVATECACHE = ffi::SQLITE_OPEN_PRIVATECACHE;
/// The database connection comes up in "extended result code mode". In other
/// words, the database behaves has if
/// [ffi::sqlite3_extended_result_codes](db,1) where called on the database
/// connection as soon as the connection is created. In addition to setting the
/// extended result code mode, this flag also causes the corresponding
/// [Database] open method to return an extended result code.
const EXRESCODE = ffi::SQLITE_OPEN_EXRESCODE;
/// The database filename is not allowed to be a symbolic link.
const NOFOLLOW = ffi::SQLITE_OPEN_NOFOLLOW;
/// This is the set of flags used when calling open methods that do not accept
/// flags.
const DEFAULT = ffi::SQLITE_OPEN_READWRITE | ffi::SQLITE_OPEN_CREATE;
}
}
/// Represents a borrowed connection to an SQLite database.
#[repr(transparent)]
pub struct Connection {
db: ffi::sqlite3,
}
impl Connection {
/// Convert an SQLite handle into a reference to Connection.
///
/// # Safety
///
/// The behavior of this method is undefined if the passed pointer is not valid.
pub unsafe fn from_ptr<'a>(db: *mut ffi::sqlite3) -> &'a mut Connection {
&mut *(db as *mut Connection)
}
/// Get the underlying SQLite handle.
///
/// # Safety
///
/// Using the returned pointer may cause undefined behavior in other, safe code.
pub unsafe fn as_mut_ptr(&self) -> *mut ffi::sqlite3 {
&self.db as *const _ as _
}
/// Load the extension at the given path, optionally providing a specific entry point.
///
/// # Safety
///
/// Loading libraries can cause undefined behavior in safe code. It is the caller's
/// responsibility to ensure that the extension is compatible with sqlite3_ext. In
/// particular, the caller must verify that the extension being loaded (and the entry
/// point being invoked) is actually an SQLite extension (created with sqlite3_ext or
/// otherwise). Never invoke this method with untrusted user-specified data.
///
/// If extension loading is not enabled when this method is called and SQLite is at
/// least version 3.13.0, then extension loading will temporarily be enabled before
/// loading the extension, and disabled afterwards. On older versions of SQLite,
/// extension loading must be manually enabled using unsafe ffi functions before this
/// method can be used, see
/// [sqlite3_enable_load_extension](https://www.sqlite.org/c3ref/enable_load_extension.html)
/// for details.
///
/// Requires SQLite 3.8.7.
pub fn load_extension(&self, path: &str, entry: Option<&str>) -> Result<()> {
let _ = (path, entry);
sqlite3_require_version!(3_008_007, {
let guard = self.lock();
LoadExtensionGuard::new(&guard)?;
unsafe {
let mut err: MaybeUninit<*mut i8> = MaybeUninit::uninit();
let path = CString::new(path)?;
let entry = match entry {
Some(s) => Some(CString::new(s)?),
None => None,
};
let rc = ffi::sqlite3_load_extension(
guard.as_mut_ptr(),
path.as_ptr(),
entry.map_or_else(null, |s| s.as_ptr()),
err.as_mut_ptr(),
);
if rc != ffi::SQLITE_OK {
let err = NonNull::new(err.assume_init()).and_then(|err| {
let ret = CStr::from_ptr(err.as_ptr()).to_str().ok().map(String::from);
ffi::sqlite3_free(err.as_ptr() as _);
ret
});
Err(Error::Sqlite(rc, err))
} else {
Ok(())
}
}
})
}
/// Enable or disable the "defensive" flag for the database.
///
/// See
/// [SQLITE_DBCONFIG_DEFENSIVE](https://www.sqlite.org/c3ref/c_dbconfig_defensive.html#sqlitedbconfigdefensive)
/// for details.
///
/// Requires SQLite 3.26.0. On earlier versions, this method is a no-op.
pub fn db_config_defensive(&self, enable: bool) -> Result<()> {
let _ = enable;
sqlite3_match_version! {
3_026_000 => unsafe {
Error::from_sqlite_desc_unchecked(
ffi::sqlite3_db_config()(
self.as_mut_ptr(),
ffi::SQLITE_DBCONFIG_DEFENSIVE,
enable as i32,
0_i32,
),
self.as_mut_ptr(),
)
},
_ => Ok(()),
}
}
/// Prints the text of all currently prepared statements to stderr. Intended for
/// debugging.
pub fn dump_prepared_statements(&self) {
unsafe {
let mut stmt = ffi::sqlite3_next_stmt(self.as_mut_ptr(), std::ptr::null_mut());
while !stmt.is_null() {
let cstr = CStr::from_ptr(ffi::sqlite3_sql(stmt)).to_str();
match cstr {
Ok(cstr) => eprintln!("=> {cstr}"),
Err(e) => eprintln!("{stmt:?}: invalid SQL: {e}"),
}
stmt = ffi::sqlite3_next_stmt(self.as_mut_ptr(), stmt);
}
}
}
}
impl std::fmt::Debug for Connection {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("Connection").finish_non_exhaustive()
}
}
#[cfg(unix)]
fn path_to_cstring(path: &Path) -> CString {
use std::os::unix::ffi::OsStrExt;
CString::new(path.as_os_str().as_bytes()).unwrap()
}
/// Represents an owned connection to an SQLite database.
///
/// This struct is an owned version of [Connection]. When this struct is dropped, it will close
/// the underlying connection to SQLite.
pub struct Database {
db: *mut ffi::sqlite3,
}
impl Database {
pub fn open<P: AsRef<Path>>(path: P) -> Result<Database> {
let filename = path_to_cstring(path.as_ref());
Database::_open(filename.as_c_str(), OpenFlags::DEFAULT)
}
pub fn open_with_flags<P: AsRef<Path>>(path: P, flags: OpenFlags) -> Result<Database> {
let filename = path_to_cstring(path.as_ref());
Database::_open(filename.as_c_str(), flags)
}
fn _open(filename: &CStr, flags: OpenFlags) -> Result<Database> {
let mut db = MaybeUninit::uninit();
let rc = Error::from_sqlite(unsafe {
ffi::sqlite3_open_v2(
filename.as_ptr() as _,
db.as_mut_ptr(),
flags.bits,
null_mut(),
)
});
match rc {
Ok(()) => Ok(Database {
db: unsafe { *db.as_ptr() },
}),
Err(e) => {
if !db.as_ptr().is_null() {
// Panic if we can't close the database we failed to open
Error::from_sqlite(unsafe { ffi::sqlite3_close(*db.as_ptr()) }).unwrap();
}
Err(e)
}
}
}
/// Gracefully close the database. This automatically happens when the Database is
/// dropped, but a failure in drop will result in a panic, while this method provides a
/// path for graceful error handling.
pub fn close(mut self) -> std::result::Result<(), (Error, Database)> {
match self._close() {
Ok(()) => Ok(()),
Err(e) => Err((e, self)),
}
}
fn _close(&mut self) -> Result<()> {
Error::from_sqlite(unsafe { ffi::sqlite3_close(self.db) })?;
self.db = null_mut();
Ok(())
}
}
impl std::fmt::Debug for Database {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("Database").finish_non_exhaustive()
}
}
impl Drop for Database {
fn drop(&mut self) {
if let Err(e) = self._close() {
if panicking() {
eprintln!("Error while closing SQLite connection: {e:?}");
} else {
panic!("Error while closing SQLite connection: {e:?}");
}
}
}
}
impl Deref for Database {
type Target = Connection;
fn deref(&self) -> &Connection {
unsafe { Connection::from_ptr(self.db) }
}
}
impl DerefMut for Database {
fn deref_mut(&mut self) -> &mut Connection {
unsafe { Connection::from_ptr(self.db) }
}
}
#[cfg(modern_sqlite)]
struct LoadExtensionGuard<'a> {
db: &'a SQLiteMutexGuard<'a, Connection>,
was_enabled: bool,
}
#[cfg(modern_sqlite)]
impl<'a> LoadExtensionGuard<'a> {
pub fn new(db: &'a SQLiteMutexGuard<'a, Connection>) -> Result<Self> {
unsafe {
let was_enabled = sqlite3_match_version! {
3_013_000 => {
let mut was_enabled: MaybeUninit<c_int> = MaybeUninit::uninit();
Error::from_sqlite(ffi::sqlite3_db_config()(
db.as_mut_ptr(),
ffi::SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION,
-1,
was_enabled.as_mut_ptr(),
))?;
Error::from_sqlite(ffi::sqlite3_db_config()(
db.as_mut_ptr(),
ffi::SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION,
1,
0,
))?;
was_enabled.assume_init() != 0
}
_ => true,
};
Ok(Self { db, was_enabled })
}
}
}
#[cfg(modern_sqlite)]
impl Drop for LoadExtensionGuard<'_> {
fn drop(&mut self) {
if !self.was_enabled {
sqlite3_match_version! {
3_013_000 => unsafe {
Error::from_sqlite(ffi::sqlite3_db_config()(
self.db.as_mut_ptr(),
ffi::SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION,
0,
0,
))
.unwrap();
},
_ => (),
}
}
}
}