1 use std::ffi::CStr;
2 use std::os::raw::{c_char, c_int};
3 #[cfg(feature = "load_extension")]
4 use std::path::Path;
5 use std::ptr;
6 use std::str;
7 use std::sync::atomic::{AtomicBool, Ordering};
8 use std::sync::{Arc, Mutex};
9
10 use super::ffi;
11 use super::str_for_sqlite;
12 use super::{Connection, InterruptHandle, OpenFlags, Result};
13 use crate::error::{error_from_handle, error_from_sqlite_code, Error};
14 use crate::raw_statement::RawStatement;
15 use crate::statement::Statement;
16 use crate::unlock_notify;
17 use crate::version::version_number;
18
19 pub struct InnerConnection {
20 pub db: *mut ffi::sqlite3,
21 // It's unsafe to call `sqlite3_close` while another thread is performing
22 // a `sqlite3_interrupt`, and vice versa, so we take this mutex during
23 // those functions. This protects a copy of the `db` pointer (which is
24 // cleared on closing), however the main copy, `db`, is unprotected.
25 // Otherwise, a long running query would prevent calling interrupt, as
26 // interrupt would only acquire the lock after the query's completion.
27 interrupt_lock: Arc<Mutex<*mut ffi::sqlite3>>,
28 #[cfg(feature = "hooks")]
29 pub free_commit_hook: Option<unsafe fn(*mut ::std::os::raw::c_void)>,
30 #[cfg(feature = "hooks")]
31 pub free_rollback_hook: Option<unsafe fn(*mut ::std::os::raw::c_void)>,
32 #[cfg(feature = "hooks")]
33 pub free_update_hook: Option<unsafe fn(*mut ::std::os::raw::c_void)>,
34 owned: bool,
35 }
36
37 impl InnerConnection {
38 #[allow(clippy::mutex_atomic)]
new(db: *mut ffi::sqlite3, owned: bool) -> InnerConnection39 pub unsafe fn new(db: *mut ffi::sqlite3, owned: bool) -> InnerConnection {
40 InnerConnection {
41 db,
42 interrupt_lock: Arc::new(Mutex::new(db)),
43 #[cfg(feature = "hooks")]
44 free_commit_hook: None,
45 #[cfg(feature = "hooks")]
46 free_rollback_hook: None,
47 #[cfg(feature = "hooks")]
48 free_update_hook: None,
49 owned,
50 }
51 }
52
open_with_flags( c_path: &CStr, flags: OpenFlags, vfs: Option<&CStr>, ) -> Result<InnerConnection>53 pub fn open_with_flags(
54 c_path: &CStr,
55 flags: OpenFlags,
56 vfs: Option<&CStr>,
57 ) -> Result<InnerConnection> {
58 #[cfg(not(feature = "bundled"))]
59 ensure_valid_sqlite_version();
60 ensure_safe_sqlite_threading_mode()?;
61
62 // Replicate the check for sane open flags from SQLite, because the check in
63 // SQLite itself wasn't added until version 3.7.3.
64 debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_ONLY.bits, 0x02);
65 debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_WRITE.bits, 0x04);
66 debug_assert_eq!(
67 1 << (OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE).bits,
68 0x40
69 );
70 if (1 << (flags.bits & 0x7)) & 0x46 == 0 {
71 return Err(Error::SqliteFailure(
72 ffi::Error::new(ffi::SQLITE_MISUSE),
73 None,
74 ));
75 }
76
77 let z_vfs = match vfs {
78 Some(c_vfs) => c_vfs.as_ptr(),
79 None => ptr::null(),
80 };
81
82 unsafe {
83 let mut db: *mut ffi::sqlite3 = ptr::null_mut();
84 let r = ffi::sqlite3_open_v2(c_path.as_ptr(), &mut db, flags.bits(), z_vfs);
85 if r != ffi::SQLITE_OK {
86 let e = if db.is_null() {
87 error_from_sqlite_code(r, Some(c_path.to_string_lossy().to_string()))
88 } else {
89 let mut e = error_from_handle(db, r);
90 if let Error::SqliteFailure(
91 ffi::Error {
92 code: ffi::ErrorCode::CannotOpen,
93 ..
94 },
95 Some(msg),
96 ) = e
97 {
98 e = Error::SqliteFailure(
99 ffi::Error::new(r),
100 Some(format!("{}: {}", msg, c_path.to_string_lossy())),
101 );
102 }
103 ffi::sqlite3_close(db);
104 e
105 };
106
107 return Err(e);
108 }
109 let r = ffi::sqlite3_busy_timeout(db, 5000);
110 if r != ffi::SQLITE_OK {
111 let e = error_from_handle(db, r);
112 ffi::sqlite3_close(db);
113 return Err(e);
114 }
115
116 // attempt to turn on extended results code; don't fail if we can't.
117 ffi::sqlite3_extended_result_codes(db, 1);
118
119 Ok(InnerConnection::new(db, true))
120 }
121 }
122
db(&self) -> *mut ffi::sqlite3123 pub fn db(&self) -> *mut ffi::sqlite3 {
124 self.db
125 }
126
decode_result(&mut self, code: c_int) -> Result<()>127 pub fn decode_result(&mut self, code: c_int) -> Result<()> {
128 unsafe { InnerConnection::decode_result_raw(self.db(), code) }
129 }
130
decode_result_raw(db: *mut ffi::sqlite3, code: c_int) -> Result<()>131 unsafe fn decode_result_raw(db: *mut ffi::sqlite3, code: c_int) -> Result<()> {
132 if code == ffi::SQLITE_OK {
133 Ok(())
134 } else {
135 Err(error_from_handle(db, code))
136 }
137 }
138
139 #[allow(clippy::mutex_atomic)]
close(&mut self) -> Result<()>140 pub fn close(&mut self) -> Result<()> {
141 if self.db.is_null() {
142 return Ok(());
143 }
144 self.remove_hooks();
145 let mut shared_handle = self.interrupt_lock.lock().unwrap();
146 assert!(
147 !shared_handle.is_null(),
148 "Bug: Somehow interrupt_lock was cleared before the DB was closed"
149 );
150 if !self.owned {
151 self.db = ptr::null_mut();
152 return Ok(());
153 }
154 unsafe {
155 let r = ffi::sqlite3_close(self.db);
156 // Need to use _raw because _guard has a reference out, and
157 // decode_result takes &mut self.
158 let r = InnerConnection::decode_result_raw(self.db, r);
159 if r.is_ok() {
160 *shared_handle = ptr::null_mut();
161 self.db = ptr::null_mut();
162 }
163 r
164 }
165 }
166
get_interrupt_handle(&self) -> InterruptHandle167 pub fn get_interrupt_handle(&self) -> InterruptHandle {
168 InterruptHandle {
169 db_lock: Arc::clone(&self.interrupt_lock),
170 }
171 }
172
execute_batch(&mut self, sql: &str) -> Result<()>173 pub fn execute_batch(&mut self, sql: &str) -> Result<()> {
174 // use CString instead of SmallCString because it's probably big.
175 let c_sql = std::ffi::CString::new(sql)?;
176 unsafe {
177 let r = ffi::sqlite3_exec(
178 self.db(),
179 c_sql.as_ptr(),
180 None,
181 ptr::null_mut(),
182 ptr::null_mut(),
183 );
184 self.decode_result(r)
185 }
186 }
187
188 #[cfg(feature = "load_extension")]
enable_load_extension(&mut self, onoff: c_int) -> Result<()>189 pub fn enable_load_extension(&mut self, onoff: c_int) -> Result<()> {
190 let r = unsafe { ffi::sqlite3_enable_load_extension(self.db, onoff) };
191 self.decode_result(r)
192 }
193
194 #[cfg(feature = "load_extension")]
load_extension(&self, dylib_path: &Path, entry_point: Option<&str>) -> Result<()>195 pub fn load_extension(&self, dylib_path: &Path, entry_point: Option<&str>) -> Result<()> {
196 let dylib_str = super::path_to_cstring(dylib_path)?;
197 unsafe {
198 let mut errmsg: *mut c_char = ptr::null_mut();
199 let r = if let Some(entry_point) = entry_point {
200 let c_entry = crate::str_to_cstring(entry_point)?;
201 ffi::sqlite3_load_extension(
202 self.db,
203 dylib_str.as_ptr(),
204 c_entry.as_ptr(),
205 &mut errmsg,
206 )
207 } else {
208 ffi::sqlite3_load_extension(self.db, dylib_str.as_ptr(), ptr::null(), &mut errmsg)
209 };
210 if r == ffi::SQLITE_OK {
211 Ok(())
212 } else {
213 let message = super::errmsg_to_string(errmsg);
214 ffi::sqlite3_free(errmsg as *mut ::std::os::raw::c_void);
215 Err(error_from_sqlite_code(r, Some(message)))
216 }
217 }
218 }
219
last_insert_rowid(&self) -> i64220 pub fn last_insert_rowid(&self) -> i64 {
221 unsafe { ffi::sqlite3_last_insert_rowid(self.db()) }
222 }
223
prepare<'a>(&mut self, conn: &'a Connection, sql: &str) -> Result<Statement<'a>>224 pub fn prepare<'a>(&mut self, conn: &'a Connection, sql: &str) -> Result<Statement<'a>> {
225 let mut c_stmt = ptr::null_mut();
226 let (c_sql, len, _) = str_for_sqlite(sql.as_bytes())?;
227 let mut c_tail = ptr::null();
228 let r = unsafe {
229 if cfg!(feature = "unlock_notify") {
230 let mut rc;
231 loop {
232 rc = ffi::sqlite3_prepare_v2(
233 self.db(),
234 c_sql,
235 len,
236 &mut c_stmt as *mut *mut ffi::sqlite3_stmt,
237 &mut c_tail as *mut *const c_char,
238 );
239 if !unlock_notify::is_locked(self.db, rc) {
240 break;
241 }
242 rc = unlock_notify::wait_for_unlock_notify(self.db);
243 if rc != ffi::SQLITE_OK {
244 break;
245 }
246 }
247 rc
248 } else {
249 ffi::sqlite3_prepare_v2(
250 self.db(),
251 c_sql,
252 len,
253 &mut c_stmt as *mut *mut ffi::sqlite3_stmt,
254 &mut c_tail as *mut *const c_char,
255 )
256 }
257 };
258 // If there is an error, *ppStmt is set to NULL.
259 self.decode_result(r)?;
260 // If the input text contains no SQL (if the input is an empty string or a
261 // comment) then *ppStmt is set to NULL.
262 let c_stmt: *mut ffi::sqlite3_stmt = c_stmt;
263 let c_tail: *const c_char = c_tail;
264 // TODO ignore spaces, comments, ... at the end
265 let tail = !c_tail.is_null() && unsafe { c_tail != c_sql.offset(len as isize) };
266 Ok(Statement::new(conn, unsafe {
267 RawStatement::new(c_stmt, tail)
268 }))
269 }
270
changes(&mut self) -> usize271 pub fn changes(&mut self) -> usize {
272 unsafe { ffi::sqlite3_changes(self.db()) as usize }
273 }
274
is_autocommit(&self) -> bool275 pub fn is_autocommit(&self) -> bool {
276 unsafe { ffi::sqlite3_get_autocommit(self.db()) != 0 }
277 }
278
279 #[cfg(feature = "modern_sqlite")] // 3.8.6
is_busy(&self) -> bool280 pub fn is_busy(&self) -> bool {
281 let db = self.db();
282 unsafe {
283 let mut stmt = ffi::sqlite3_next_stmt(db, ptr::null_mut());
284 while !stmt.is_null() {
285 if ffi::sqlite3_stmt_busy(stmt) != 0 {
286 return true;
287 }
288 stmt = ffi::sqlite3_next_stmt(db, stmt);
289 }
290 }
291 false
292 }
293
294 #[cfg(not(feature = "hooks"))]
remove_hooks(&mut self)295 fn remove_hooks(&mut self) {}
296 }
297
298 impl Drop for InnerConnection {
299 #[allow(unused_must_use)]
drop(&mut self)300 fn drop(&mut self) {
301 use std::thread::panicking;
302
303 if let Err(e) = self.close() {
304 if panicking() {
305 eprintln!("Error while closing SQLite connection: {:?}", e);
306 } else {
307 panic!("Error while closing SQLite connection: {:?}", e);
308 }
309 }
310 }
311 }
312
313 #[cfg(not(feature = "bundled"))]
314 static SQLITE_VERSION_CHECK: std::sync::Once = std::sync::Once::new();
315 #[cfg(not(feature = "bundled"))]
316 pub static BYPASS_VERSION_CHECK: AtomicBool = AtomicBool::new(false);
317
318 #[cfg(not(feature = "bundled"))]
ensure_valid_sqlite_version()319 fn ensure_valid_sqlite_version() {
320 use crate::version::version;
321
322 SQLITE_VERSION_CHECK.call_once(|| {
323 let version_number = version_number();
324
325 // Check our hard floor.
326 if version_number < 3_006_008 {
327 panic!("rusqlite requires SQLite 3.6.8 or newer");
328 }
329
330 // Check that the major version number for runtime and buildtime match.
331 let buildtime_major = ffi::SQLITE_VERSION_NUMBER / 1_000_000;
332 let runtime_major = version_number / 1_000_000;
333 if buildtime_major != runtime_major {
334 panic!(
335 "rusqlite was built against SQLite {} but is running with SQLite {}",
336 str::from_utf8(ffi::SQLITE_VERSION).unwrap(),
337 version()
338 );
339 }
340
341 if BYPASS_VERSION_CHECK.load(Ordering::Relaxed) {
342 return;
343 }
344
345 // Check that the runtime version number is compatible with the version number
346 // we found at build-time.
347 if version_number < ffi::SQLITE_VERSION_NUMBER {
348 panic!(
349 "\
350 rusqlite was built against SQLite {} but the runtime SQLite version is {}. To fix this, either:
351 * Recompile rusqlite and link against the SQLite version you are using at runtime, or
352 * Call rusqlite::bypass_sqlite_version_check() prior to your first connection attempt. Doing this
353 means you're sure everything will work correctly even though the runtime version is older than
354 the version we found at build time.",
355 str::from_utf8(ffi::SQLITE_VERSION).unwrap(),
356 version()
357 );
358 }
359 });
360 }
361
362 #[cfg(not(any(target_arch = "wasm32")))]
363 static SQLITE_INIT: std::sync::Once = std::sync::Once::new();
364
365 pub static BYPASS_SQLITE_INIT: AtomicBool = AtomicBool::new(false);
366
367 // threading mode checks are not necessary (and do not work) on target
368 // platforms that do not have threading (such as webassembly)
369 #[cfg(any(target_arch = "wasm32"))]
ensure_safe_sqlite_threading_mode() -> Result<()>370 fn ensure_safe_sqlite_threading_mode() -> Result<()> {
371 Ok(())
372 }
373
374 #[cfg(not(any(target_arch = "wasm32")))]
ensure_safe_sqlite_threading_mode() -> Result<()>375 fn ensure_safe_sqlite_threading_mode() -> Result<()> {
376 // Ensure SQLite was compiled in thredsafe mode.
377 if unsafe { ffi::sqlite3_threadsafe() == 0 } {
378 return Err(Error::SqliteSingleThreadedMode);
379 }
380
381 // Now we know SQLite is _capable_ of being in Multi-thread of Serialized mode,
382 // but it's possible someone configured it to be in Single-thread mode
383 // before calling into us. That would mean we're exposing an unsafe API via
384 // a safe one (in Rust terminology), which is no good. We have two options
385 // to protect against this, depending on the version of SQLite we're linked
386 // with:
387 //
388 // 1. If we're on 3.7.0 or later, we can ask SQLite for a mutex and check for
389 // the magic value 8. This isn't documented, but it's what SQLite
390 // returns for its mutex allocation function in Single-thread mode.
391 // 2. If we're prior to SQLite 3.7.0, AFAIK there's no way to check the
392 // threading mode. The check we perform for >= 3.7.0 will segfault.
393 // Instead, we insist on being able to call sqlite3_config and
394 // sqlite3_initialize ourself, ensuring we know the threading
395 // mode. This will fail if someone else has already initialized SQLite
396 // even if they initialized it safely. That's not ideal either, which is
397 // why we expose bypass_sqlite_initialization above.
398 if version_number() >= 3_007_000 {
399 const SQLITE_SINGLETHREADED_MUTEX_MAGIC: usize = 8;
400 let is_singlethreaded = unsafe {
401 let mutex_ptr = ffi::sqlite3_mutex_alloc(0);
402 let is_singlethreaded = mutex_ptr as usize == SQLITE_SINGLETHREADED_MUTEX_MAGIC;
403 ffi::sqlite3_mutex_free(mutex_ptr);
404 is_singlethreaded
405 };
406 if is_singlethreaded {
407 Err(Error::SqliteSingleThreadedMode)
408 } else {
409 Ok(())
410 }
411 } else {
412 SQLITE_INIT.call_once(|| {
413 if BYPASS_SQLITE_INIT.load(Ordering::Relaxed) {
414 return;
415 }
416
417 unsafe {
418 let msg = "\
419 Could not ensure safe initialization of SQLite.
420 To fix this, either:
421 * Upgrade SQLite to at least version 3.7.0
422 * Ensure that SQLite has been initialized in Multi-thread or Serialized mode and call
423 rusqlite::bypass_sqlite_initialization() prior to your first connection attempt.";
424
425 if ffi::sqlite3_config(ffi::SQLITE_CONFIG_MULTITHREAD) != ffi::SQLITE_OK {
426 panic!(msg);
427 }
428 if ffi::sqlite3_initialize() != ffi::SQLITE_OK {
429 panic!(msg);
430 }
431 }
432 });
433 Ok(())
434 }
435 }
436