1#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
2extern crate libsqlite3_sys as ffi;
3#[cfg(all(target_family = "wasm", target_os = "unknown"))]
4use sqlite_wasm_rs as ffi;
56use crate::result::Error::DatabaseError;
7use crate::result::*;
8use crate::sqlite::SqliteConnection;
9use alloc::boxed::Box;
10use alloc::string::{String, ToString};
11use core::ffi::{c_char, c_int};
12#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
13type RawApiPointer = *const core::ffi::c_void;
14#[cfg(all(target_family = "wasm", target_os = "unknown"))]
15type RawApiPointer = *const ffi::sqlite3_api_routines;
1617/// SQLite's auto-extension callback type.
18type RawAutoExtension = unsafe extern "C" fn(
19 db: *mut ffi::sqlite3,
20 pz_err_msg: *mut *mut c_char,
21 p_api: RawApiPointer,
22) -> c_int;
2324// TODO: diesel 3.0 use `libsqlite3-sys` declarations after raising the minimum to 0.29.
25#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
26mod auto_extension_ffi {
27use super::{RawAutoExtension, c_int};
2829// SAFETY: These declarations match SQLite's C ABI and bypass incompatible callback types in older bindings.
30#[allow(unsafe_code)]
31unsafe extern "C" {
32pub(super) fn sqlite3_auto_extension(entry_point: Option<RawAutoExtension>) -> c_int;
33pub(super) fn sqlite3_cancel_auto_extension(entry_point: Option<RawAutoExtension>)
34 -> c_int;
35 }
36}
3738#[cfg(all(target_family = "wasm", target_os = "unknown"))]
39use ffi as auto_extension_ffi;
4041/// Registers an auto-extension that runs for every SQLite connection opened in
42/// this process, including non-Diesel ones.
43///
44/// This is a safe wrapper around [`sqlite3_auto_extension`][docs]. The callback
45/// receives the [`SqliteConnection`] being opened and returns `Ok(())` to
46/// continue or an error to fail the open. Use it to register SQL functions,
47/// collations, or aggregates through the usual connection API, or to initialize
48/// a statically linked C extension such as Spatialite or sqlite-vec via
49/// [`SqliteConnection::with_raw_connection`].
50///
51/// Call this before opening any connection. Extensions run in registration
52/// order, and the first error aborts the open. The callback must be a `fn` item
53/// or a closure that captures only zero-sized values (enforced at compile
54/// time), and registering the same `fn` twice is a no-op. It may run on several
55/// threads at once and must not open another connection (which would re-enter
56/// the auto-extensions and recurse) or call [`register_auto_extension`],
57/// [`cancel_auto_extension`], or [`reset_auto_extension`]. Panics are caught and
58/// turned into a failed open.
59///
60/// [docs]: https://www.sqlite.org/c3ref/auto_extension.html
61///
62/// # Example
63///
64/// ```rust
65/// # // This test registers Rust callbacks that sqlite invokes through the
66/// # // FFI boundary, which is not supported by miri `-Zmiri-native-lib`.
67/// # #[cfg(not(miri))]
68/// # fn main() {
69/// use diesel::dsl::sql;
70/// use diesel::prelude::*;
71/// use diesel::sql_types::Integer;
72/// use diesel::sqlite::{register_auto_extension, reset_auto_extension, SqliteConnection};
73///
74/// // Registers a case-insensitive collation on every new connection.
75/// fn my_ext(conn: &mut SqliteConnection) -> QueryResult<()> {
76/// conn.register_collation("RUSTNOCASE", |a, b| a.to_lowercase().cmp(&b.to_lowercase()))
77/// }
78///
79/// register_auto_extension(my_ext).unwrap();
80///
81/// // Every future connection now has the collation.
82/// let mut conn = SqliteConnection::establish(":memory:").unwrap();
83/// let equal: i32 = sql::<Integer>("SELECT 'a' = 'A' COLLATE RUSTNOCASE")
84/// .get_result(&mut conn)
85/// .unwrap();
86/// assert_eq!(equal, 1);
87/// # reset_auto_extension();
88/// # }
89/// # #[cfg(miri)]
90/// # fn main() {}
91/// ```
92#[allow(unsafe_code)]
93pub fn register_auto_extension<F>(extension: F) -> QueryResult<()>
94where
95F: Fn(&mut SqliteConnection) -> QueryResult<()> + Sync + 'static,
96{
97// SAFETY: `entry_point` returns a stable function pointer with SQLite's callback ABI.
98let result =
99unsafe { auto_extension_ffi::sqlite3_auto_extension(Some(entry_point(extension))) };
100if result == ffi::SQLITE_OK {
101Ok(())
102 } else {
103Err(DatabaseError(
104 DatabaseErrorKind::Unknown,
105Box::new(ffi::code_to_str(result).to_string()),
106 ))
107 }
108}
109110/// Removes a previously registered auto-extension, returning `true` if it was
111/// found ([docs][cancel_docs]).
112///
113/// Pass the same `fn` item given to [`register_auto_extension`]. A closure
114/// cannot be cancelled this way, because its type cannot be named again. Use
115/// [`reset_auto_extension`] to clear everything instead.
116///
117/// [cancel_docs]: https://www.sqlite.org/c3ref/cancel_auto_extension.html
118#[allow(unsafe_code)]
119pub fn cancel_auto_extension<F>(extension: F) -> bool120where
121F: Fn(&mut SqliteConnection) -> QueryResult<()> + Sync + 'static,
122{
123// SAFETY: `entry_point` returns the same stable pointer used for registration.
124unsafe { auto_extension_ffi::sqlite3_cancel_auto_extension(Some(entry_point(extension))) != 0 }
125}
126127/// Clears **all** registered auto-extensions ([docs][reset_docs]).
128///
129/// After this call, no auto-extensions will run for newly opened connections.
130///
131/// [reset_docs]: https://www.sqlite.org/c3ref/reset_auto_extension.html
132#[allow(unsafe_code)]
133pub fn reset_auto_extension() {
134unsafe { ffi::sqlite3_reset_auto_extension() }
135}
136137/// Returns the trampoline for `F`. `extension` is taken by value to infer `F`,
138/// then `forget`-ten so the zero-sized callback stays conceptually alive for the
139/// process and its destructor never runs.
140fn entry_point<F>(extension: F) -> RawAutoExtension141where
142F: Fn(&mut SqliteConnection) -> QueryResult<()> + Sync + 'static,
143{
144 core::mem::forget(extension);
145trampoline::<F>
146}
147148/// The C entry point handed to SQLite, monomorphized per callback type `F` so
149/// each distinct callback maps to a distinct, stable address. SQLite's
150/// pointer-based deduplication and [`cancel_auto_extension`] rely on that.
151#[allow(unsafe_code)]
152unsafe extern "C" fn trampoline<F>(
153 db: *mut ffi::sqlite3,
154 pz_err_msg: *mut *mut c_char,
155 _p_api: RawApiPointer,
156) -> c_int157where
158F: Fn(&mut SqliteConnection) -> QueryResult<()> + Sync + 'static,
159{
160const {
161if !(core::mem::size_of::<F>() == 0) {
{
::core::panicking::panic_fmt(format_args!("an auto-extension callback must not capture non-zero-sized state. Use a `fn` item or a closure that captures only zero-sized values"));
}
};assert!(
162 core::mem::size_of::<F>() == 0,
163"an auto-extension callback must not capture non-zero-sized state. \
164 Use a `fn` item or a closure that captures only zero-sized values"
165);
166 }
167168// `_p_api` matters only for runtime-loaded shared libraries. Statically
169 // linked extensions link the SQLite symbols directly, so we ignore it.
170let result: Result<(), String> =
171crate::util::std_compat::catch_unwind(core::panic::AssertUnwindSafe(|| {
172let Some(db) = core::ptr::NonNull::new(db) else {
173return Err(String::from(
174"auto-extension received a null database handle",
175 ));
176 };
177// Reconstruct a *reference* to the zero-sized callback, never an
178 // owned value, so its destructor never runs. `&F: Fn` because `F: Fn`.
179 // SAFETY: `F` is zero-sized (asserted above), so a dangling, aligned,
180 // non-null pointer is a valid `&F`, which `NonNull::dangling` provides.
181let extension: &F = unsafe { core::ptr::NonNull::<F>::dangling().as_ref() };
182// SAFETY: `db` is a valid handle for the duration of this call, and
183 // the borrowed connection does not take ownership of it.
184unsafe { SqliteConnection::with_borrowed_connection(db, extension) }
185 .map_err(|e| e.to_string())
186 }))
187 .unwrap_or_else(|panic| {
188Err(match panic_detail(panic) {
189Some(message) => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("auto-extension panicked: {0}",
message))
})alloc::format!("auto-extension panicked: {message}"),
190None => String::from("auto-extension panicked"),
191 })
192 });
193194match result {
195Ok(()) => ffi::SQLITE_OK,
196Err(message) => {
197set_error_message(pz_err_msg, &message);
198 ffi::SQLITE_ERROR199 }
200 }
201}
202203/// Best-effort message from a caught panic payload. The no_std `catch_unwind`
204/// carries no payload, so only the `std` variant can recover the text.
205#[cfg(feature = "std")]
206fn panic_detail(panic: alloc::boxed::Box<dyn core::any::Any + Send>) -> Option<String> {
207panic208 .downcast_ref::<&str>()
209 .map(|s| (*s).to_owned())
210 .or_else(|| panic.downcast_ref::<String>().cloned())
211}
212213#[cfg(not(feature = "std"))]
214fn panic_detail(_panic: ()) -> Option<String> {
215None
216}
217218/// Writes `message` into `*pz_err_msg` with `sqlite3_malloc`, which is the
219/// allocator SQLite later frees it with. The message is truncated at the first
220/// NUL byte to form a valid C string, and allocation failure is ignored.
221#[allow(unsafe_code)]
222fn set_error_message(pz_err_msg: *mut *mut c_char, message: &str) {
223if pz_err_msg.is_null() {
224return;
225 }
226227let bytes = message.as_bytes();
228let len = bytes.iter().position(|&b| b == 0).unwrap_or(bytes.len());
229230// SQLite sizes allocations with a C `int`. A message that does not fit is
231 // dropped rather than truncated to a bogus length.
232let Ok(size) = c_int::try_from(len + 1) else {
233return;
234 };
235let buffer = unsafe { ffi::sqlite3_malloc(size) } as *mut u8;
236if buffer.is_null() {
237return;
238 }
239240unsafe {
241 core::ptr::copy_nonoverlapping(bytes.as_ptr(), buffer, len);
242*buffer.add(len) = 0;
243*pz_err_msg = bufferas *mut c_char;
244 }
245}
246247// These tests either rely on sqlite calling a registered auto extension, or
248// read an error message allocated by the native library, neither of which is
249// supported when running under miri with a native libsqlite3
250// (`-Zmiri-native-lib`), so the whole module is compiled out in that case.
251#[cfg(all(test, not(miri)))]
252mod tests {
253use super::*;
254use crate::dsl::sql;
255use crate::prelude::*;
256use crate::sql_types::Integer;
257use std::sync::Mutex;
258259// `sqlite3_auto_extension` is process-global, so these tests serialize on
260 // this lock and register only benign (never-failing) extensions, leaving
261 // connections opened by other tests unaffected. The failing path is covered
262 // by `trampoline_maps_result_to_return_code`, which calls the trampoline
263 // directly without touching the global registry.
264static AUTO_EXT_TEST_LOCK: Mutex<()> = Mutex::new(());
265266// A benign auto-extension: registers a `TESTCOLL` collation through the
267 // normal connection API.
268fn test_ext_init(conn: &mut SqliteConnection) -> QueryResult<()> {
269 conn.register_collation("TESTCOLL", |a, b| a.cmp(b))
270 }
271272fn open_memory_connection() -> SqliteConnection {
273 SqliteConnection::establish(":memory:").expect("Failed to open :memory: connection")
274 }
275276// Errors out if `TESTCOLL` is not registered on a freshly opened connection.
277fn probe_collation() -> QueryResult<i32> {
278let mut conn = open_memory_connection();
279 sql::<Integer>("SELECT 'a' = 'a' COLLATE TESTCOLL").get_result(&mut conn)
280 }
281282/// RAII guard that calls `reset_auto_extension()` on drop, ensuring global
283 /// state is cleaned up even if a test panics.
284struct TestResetGuard;
285286impl Drop for TestResetGuard {
287fn drop(&mut self) {
288 reset_auto_extension();
289 }
290 }
291292#[test]
293fn auto_extension_lifecycle() {
294let _lock = AUTO_EXT_TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
295let _guard = TestResetGuard;
296 reset_auto_extension();
297298// -- 1. register + new connection has the collation --
299register_auto_extension(test_ext_init).unwrap();
300assert_eq!(probe_collation().unwrap(), 1);
301302// -- 2. cancel + new connection does NOT have the collation --
303let removed = cancel_auto_extension(test_ext_init);
304assert!(
305 removed,
306"cancel should return true for registered extension"
307);
308assert!(
309 probe_collation().is_err(),
310"collation should not be available after cancel"
311);
312313// -- 3. cancel returns false for unregistered --
314let removed = cancel_auto_extension(test_ext_init);
315assert!(
316 !removed,
317"cancel should return false for unregistered extension"
318);
319320// -- 4. reset clears all --
321register_auto_extension(test_ext_init).unwrap();
322 reset_auto_extension();
323assert!(
324 probe_collation().is_err(),
325"collation should not be available after reset"
326);
327328// -- 5. duplicate registration is idempotent --
329register_auto_extension(test_ext_init).unwrap();
330 register_auto_extension(test_ext_init).unwrap();
331assert_eq!(probe_collation().unwrap(), 1);
332// _guard drops here, ensuring reset even on panic.
333}
334335// Drives the trampoline directly (via `entry_point`), without registering
336 // it in SQLite's global list, so the Ok/Err/null paths can be checked
337 // deterministically without affecting connections opened by other tests.
338#[test]
339 #[allow(unsafe_code)]
340fn trampoline_maps_result_to_return_code() {
341fn ok_ext(_conn: &mut SqliteConnection) -> QueryResult<()> {
342Ok(())
343 }
344fn err_ext(_conn: &mut SqliteConnection) -> QueryResult<()> {
345Err(Error::QueryBuilderError("boom".into()))
346 }
347348let ok_tramp = entry_point(ok_ext);
349let err_tramp = entry_point(err_ext);
350351let mut conn = open_memory_connection();
352// SAFETY: the pointer is only used for the duration of the closure,
353 // while `conn` is alive.
354unsafe {
355 conn.with_raw_connection(|db| {
356let mut err: *mut c_char = core::ptr::null_mut();
357358// Ok -> SQLITE_OK, no error message allocated.
359let rc = ok_tramp(db, &mut err, core::ptr::null());
360assert_eq!(rc, ffi::SQLITE_OK);
361assert!(err.is_null());
362363// Err -> SQLITE_ERROR, message written via sqlite3_malloc.
364let rc = err_tramp(db, &mut err, core::ptr::null());
365assert_eq!(rc, ffi::SQLITE_ERROR);
366assert!(!err.is_null());
367let message = core::ffi::CStr::from_ptr(err)
368 .to_string_lossy()
369 .into_owned();
370assert_eq!(message, "boom");
371 ffi::sqlite3_free(err as *mut core::ffi::c_void);
372373// Null db handle -> SQLITE_ERROR, never dereferenced.
374let mut err: *mut c_char = core::ptr::null_mut();
375let rc = ok_tramp(core::ptr::null_mut(), &mut err, core::ptr::null());
376assert_eq!(rc, ffi::SQLITE_ERROR);
377if !err.is_null() {
378 ffi::sqlite3_free(err as *mut core::ffi::c_void);
379 }
380 })
381 }
382 }
383384// Regression test for the closure-reconstruction soundness fix. The callback
385 // captures a zero-sized guard with a load-bearing `Drop` that must never run,
386 // because the trampoline only reproduces the callback behind a reference and
387 // `forget`s the registered value (the old `mem::zeroed()` ran it repeatedly).
388#[test]
389fn callback_zero_sized_capture_is_never_dropped() {
390use std::sync::atomic::{AtomicUsize, Ordering};
391392static DROPS: AtomicUsize = AtomicUsize::new(0);
393394// Zero-sized, `Sync`, with a load-bearing `Drop`.
395struct Guard;
396impl Drop for Guard {
397fn drop(&mut self) {
398 DROPS.fetch_add(1, Ordering::SeqCst);
399 }
400 }
401402let _lock = AUTO_EXT_TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
403let _reset = TestResetGuard;
404 reset_auto_extension();
405406let guard = Guard;
407// `move` captures the zero-sized `guard` by value, so the closure is a
408 // zero-sized type with a non-trivial destructor.
409register_auto_extension(move |conn: &mut SqliteConnection| {
410let _ = &guard;
411 conn.register_collation("TESTCOLL", |a, b| a.cmp(b))
412 })
413 .unwrap();
414415for _ in 0..5 {
416assert_eq!(probe_collation().unwrap(), 1);
417 }
418 reset_auto_extension();
419420assert_eq!(
421 DROPS.load(Ordering::SeqCst),
4220,
423"the captured guard's destructor must never run"
424);
425 }
426427// A panicking callback becomes `SQLITE_ERROR` with the payload recovered
428 // into the message (`&str` and `String` payloads, generic fallback
429 // otherwise), and drives the panic-unwind path through the drop guard. Gated
430 // off WASM, where `catch_unwind` aborts because `panic = "abort"`.
431#[test]
432 #[allow(unsafe_code)]
433 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
434fn trampoline_reports_panic_message() {
435fn panic_str(_conn: &mut SqliteConnection) -> QueryResult<()> {
436panic!("boom-str");
437 }
438fn panic_string(_conn: &mut SqliteConnection) -> QueryResult<()> {
439panic!("boom-{}", 7);
440 }
441fn panic_other(_conn: &mut SqliteConnection) -> QueryResult<()> {
442 std::panic::panic_any(7_u8);
443 }
444445let cases: [(RawAutoExtension, &str); 3] = [
446 (entry_point(panic_str), "auto-extension panicked: boom-str"),
447 (entry_point(panic_string), "auto-extension panicked: boom-7"),
448 (entry_point(panic_other), "auto-extension panicked"),
449 ];
450451let mut conn = open_memory_connection();
452// SAFETY: the pointer is only used while `conn` is alive.
453unsafe {
454 conn.with_raw_connection(|db| {
455for (tramp, expected) in cases {
456let mut err: *mut c_char = core::ptr::null_mut();
457let rc = tramp(db, &mut err, core::ptr::null());
458assert_eq!(rc, ffi::SQLITE_ERROR);
459assert!(!err.is_null());
460let message = core::ffi::CStr::from_ptr(err)
461 .to_string_lossy()
462 .into_owned();
463assert_eq!(message, expected);
464 ffi::sqlite3_free(err as *mut core::ffi::c_void);
465 }
466 })
467 }
468 }
469470#[test]
471 #[allow(unsafe_code)]
472fn error_message_truncates_at_interior_nul() {
473let mut err: *mut c_char = core::ptr::null_mut();
474 set_error_message(&mut err, "before\0after");
475assert!(!err.is_null());
476// SAFETY: `err` is a sqlite-allocated C string we own until we free it.
477unsafe {
478let truncated = core::ffi::CStr::from_ptr(err).to_str().unwrap();
479assert_eq!(truncated, "before");
480 ffi::sqlite3_free(err as *mut core::ffi::c_void);
481 }
482483// A null out-pointer is a no-op (must not write through it or crash).
484set_error_message(core::ptr::null_mut(), "ignored");
485 }
486}