use std::ffi::CStr; use std::os::raw::{c_char, c_int}; #[cfg(feature = "load_extension")] use std::path::Path; use std::ptr; use std::str; use std::sync::atomic::AtomicBool; use std::sync::{Arc, Mutex};
pubstruct InnerConnection { pub db: *mut ffi::sqlite3, // It's unsafe to call `sqlite3_close` while another thread is performing // a `sqlite3_interrupt`, and vice versa, so we take this mutex during // those functions. This protects a copy of the `db` pointer (which is // cleared on closing), however the main copy, `db`, is unprotected. // Otherwise, a long running query would prevent calling interrupt, as // interrupt would only acquire the lock after the query's completion.
interrupt_lock: Arc<Mutex<*mut ffi::sqlite3>>, #[cfg(feature = "hooks")] pub free_commit_hook: Option<unsafefn(*mut std::os::raw::c_void)>, #[cfg(feature = "hooks")] pub free_rollback_hook: Option<unsafefn(*mut std::os::raw::c_void)>, #[cfg(feature = "hooks")] pub free_update_hook: Option<unsafefn(*mut std::os::raw::c_void)>, #[cfg(feature = "hooks")] pub progress_handler: Option<Box<dyn FnMut() -> bool + Send>>, #[cfg(feature = "hooks")] pub authorizer: Option<crate::hooks::BoxedAuthorizer>,
owned: bool,
}
#[allow(clippy::mutex_atomic)] pubfn close(&mutself) -> Result<()> { ifself.db.is_null() { return Ok(());
} self.remove_hooks(); letmut shared_handle = self.interrupt_lock.lock().unwrap();
assert!(
!shared_handle.is_null(), "Bug: Somehow interrupt_lock was cleared before the DB was closed"
); if !self.owned { self.db = ptr::null_mut(); return Ok(());
} unsafe { let r = ffi::sqlite3_close(self.db); // Need to use _raw because _guard has a reference out, and // decode_result takes &mut self. let r = InnerConnection::decode_result_raw(self.db, r); if r.is_ok() {
*shared_handle = ptr::null_mut(); self.db = ptr::null_mut();
}
r
}
}
pubfn prepare<'a>(
&mutself,
conn: &'a Connection,
sql: &str,
flags: PrepFlags,
) -> Result<Statement<'a>> { letmut c_stmt: *mut ffi::sqlite3_stmt = ptr::null_mut(); let (c_sql, len, _) = str_for_sqlite(sql.as_bytes())?; letmut c_tail: *const c_char = ptr::null(); // TODO sqlite3_prepare_v3 (https://sqlite.org/c3ref/c_prepare_normalize.html) // 3.20.0, #728 #[cfg(not(feature = "unlock_notify"))] let r = unsafe { self.prepare_(c_sql, len, flags, &mut c_stmt, &style='color:red'>mut c_tail) }; #[cfg(feature = "unlock_notify")] let r = unsafe { usecrate::unlock_notify; letmut rc; loop {
rc = self.prepare_(c_sql, len, flags, &mut c_stmt, &mut c_tail); if !unlock_notify::is_locked(self.db, rc) { break;
}
rc = unlock_notify::wait_for_unlock_notify(self.db); if rc != ffi::SQLITE_OK { break;
}
}
rc
}; // If there is an error, *ppStmt is set to NULL. if r != ffi::SQLITE_OK { return Err(unsafe { error_with_offset(self.db, r, sql) });
} // If the input text contains no SQL (if the input is an empty string or a // comment) then *ppStmt is set to NULL. let tail = if c_tail.is_null() { 0
} else { let n = (c_tail as isize) - (c_sql as isize); if n <= 0 || n >= len as isize { 0
} else {
n as usize
}
};
Ok(Statement::new(conn, unsafe {
RawStatement::new(c_stmt, tail)
}))
}
pubfn db_readonly(&self, db_name: super::DatabaseName<'_>) -> Result<bool> { let name = db_name.as_cstring()?; let r = unsafe { ffi::sqlite3_db_readonly(self.db, name.as_ptr()) }; match r { 0 => Ok(false), 1 => Ok(true),
-1 => Err(Error::SqliteFailure(
ffi::Error::new(ffi::SQLITE_MISUSE),
Some(format!("{db_name:?} is not the name of a database")),
)),
_ => Err(error_from_sqlite_code(
r,
Some("Unexpected result".to_owned()),
)),
}
}
#[cfg(feature = "modern_sqlite")] // 3.37.0 pubfn txn_state(
&self,
db_name: Option<super::DatabaseName<'_>>,
) -> Result<super::transaction::TransactionState> { let r = iflet Some(ref name) = db_name { let name = name.as_cstring()?; unsafe { ffi::sqlite3_txn_state(self.db, name.as_ptr()) }
} else { unsafe { ffi::sqlite3_txn_state(self.db, ptr::null()) }
}; match r { 0 => Ok(super::transaction::TransactionState::None), 1 => Ok(super::transaction::TransactionState::Read), 2 => Ok(super::transaction::TransactionState::Write),
-1 => Err(Error::SqliteFailure(
ffi::Error::new(ffi::SQLITE_MISUSE),
Some(format!("{db_name:?} is not the name of a valid schema")),
)),
_ => Err(error_from_sqlite_code(
r,
Some("Unexpected result".to_owned()),
)),
}
}
// threading mode checks are not necessary (and do not work) on target // platforms that do not have threading (such as webassembly) #[cfg(target_arch = "wasm32")] fn ensure_safe_sqlite_threading_mode() -> Result<()> {
Ok(())
}
// Now we know SQLite is _capable_ of being in Multi-thread of Serialized mode, // but it's possible someone configured it to be in Single-thread mode // before calling into us. That would mean we're exposing an unsafe API via // a safe one (in Rust terminology), which is no good. We have two options // to protect against this, depending on the version of SQLite we're linked // with: // // 1. If we're on 3.7.0 or later, we can ask SQLite for a mutex and check for // the magic value 8. This isn't documented, but it's what SQLite // returns for its mutex allocation function in Single-thread mode. // 2. If we're prior to SQLite 3.7.0, AFAIK there's no way to check the // threading mode. The check we perform for >= 3.7.0 will segfault. // Instead, we insist on being able to call sqlite3_config and // sqlite3_initialize ourself, ensuring we know the threading // mode. This will fail if someone else has already initialized SQLite // even if they initialized it safely. That's not ideal either, which is // why we expose bypass_sqlite_initialization above. if version_number() >= 3_007_000 { const SQLITE_SINGLETHREADED_MUTEX_MAGIC: usize = 8; let is_singlethreaded = unsafe { let mutex_ptr = ffi::sqlite3_mutex_alloc(0); let is_singlethreaded = mutex_ptr as usize == SQLITE_SINGLETHREADED_MUTEX_MAGIC;
ffi::sqlite3_mutex_free(mutex_ptr);
is_singlethreaded
}; if is_singlethreaded {
Err(Error::SqliteSingleThreadedMode)
} else {
Ok(())
}
} else { #[cfg(not(feature = "loadable_extension"))]
SQLITE_INIT.call_once(|| { use std::sync::atomic::Ordering; if BYPASS_SQLITE_INIT.load(Ordering::Relaxed) { return;
}
unsafe {
assert!(ffi::sqlite3_config(ffi::SQLITE_CONFIG_MULTITHREAD) == ffi::SQLITE_OK && ffi::sqlite3_initialize() == ffi::SQLITE_OK, "Could not ensure safe initialization of SQLite.\n\
To fix this, either:\n\
* Upgrade SQLite to at least version 3.7.0\n\
* Ensure that SQLite has been initialized in Multi-thread or Serialized mode and call\n\
rusqlite::bypass_sqlite_initialization() prior to your first connection attempt."
);
}
});
Ok(())
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.3 Sekunden
(vorverarbeitet am 2026-06-27)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.