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