Skip to main content

diesel/sqlite/connection/
raw.rs

1#![allow(unsafe_code)] // ffi calls
2#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3extern crate libsqlite3_sys as ffi;
4
5#[cfg(all(target_family = "wasm", target_os = "unknown"))]
6use sqlite_wasm_rs as ffi;
7
8use super::functions::{build_sql_function_args, process_sql_function_result};
9use super::limits::SqliteLimit;
10use super::serialized_database::SerializedDatabase;
11use super::stmt::ensure_sqlite_ok;
12use super::{CommitDecision, ProgressDecision};
13use super::{Sqlite, SqliteAggregateFunction};
14use crate::deserialize::FromSqlRow;
15use crate::result::Error::DatabaseError;
16use crate::result::*;
17use crate::serialize::ToSql;
18use crate::sql_types::HasSqlType;
19use crate::sqlite::SqliteFunctionBehavior;
20use alloc::borrow::ToOwned;
21use alloc::boxed::Box;
22use alloc::ffi::{CString, NulError};
23use alloc::string::{String, ToString};
24use core::ffi as libc;
25use core::num::NonZeroU32;
26use core::ptr::NonNull;
27use core::{mem, ptr, slice, str};
28
29// `sqlite3_db_config()` option codes controlling whether ATTACH may create new
30// database files (ATTACH_CREATE) or open them in write mode (ATTACH_WRITE).
31// Introduced in SQLite 3.49.0 / `libsqlite3-sys` 0.35.0, but Diesel supports
32// `libsqlite3-sys` >= 0.17.2, so we define them here to build against any
33// supported version. On an older linked SQLite the `sqlite3_db_config()` call
34// fails at runtime, which callers already handle.
35pub(super) const SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE: i32 = 1020;
36pub(super) const SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE: i32 = 1021;
37
38/// For use in FFI function, which cannot unwind.
39/// Print the message, ask to open an issue at Github and [`abort`](std::process::abort).
40macro_rules! assert_fail {
41    ($fmt:expr_2021 $(,$args:tt)*) => {
42        #[cfg(feature = "std")]
43        eprint!(concat!(
44            $fmt,
45            "If you see this message, please open an issue at https://github.com/diesel-rs/diesel/issues/new.\n",
46            "Source location: {}:{}\n",
47        ), $($args,)* file!(), line!());
48        crate::util::std_compat::abort()
49    };
50}
51
52#[allow(missing_debug_implementations, missing_copy_implementations)]
53pub(super) struct RawConnection {
54    pub(super) internal_connection: NonNull<ffi::sqlite3>,
55    /// Boxed closure kept alive while the commit hook is registered.
56    commit_hook: Option<Box<dyn FnMut() -> CommitDecision + Send>>,
57    /// Boxed closure kept alive while the rollback hook is registered.
58    rollback_hook: Option<Box<dyn FnMut() + Send>>,
59    /// Boxed closure kept alive while the progress handler is registered.
60    progress_hook: Option<Box<dyn FnMut() -> ProgressDecision + Send>>,
61}
62
63impl RawConnection {
64    /// Wraps a borrowed `sqlite3` pointer this `RawConnection` does not own
65    /// (kept in a `ManuallyDrop` for SQL-function callbacks, so `Drop` never runs).
66    pub(super) fn from_ptr(conn: NonNull<ffi::sqlite3>) -> Self {
67        RawConnection {
68            internal_connection: conn,
69            commit_hook: None,
70            rollback_hook: None,
71            progress_hook: None,
72        }
73    }
74
75    pub(super) fn establish(database_url: &str) -> ConnectionResult<Self> {
76        let mut conn_pointer = ptr::null_mut();
77
78        let database_url = if database_url.starts_with("sqlite://") {
79            CString::new(database_url.replacen("sqlite://", "file:", 1))?
80        } else {
81            CString::new(database_url)?
82        };
83        let flags = ffi::SQLITE_OPEN_READWRITE | ffi::SQLITE_OPEN_CREATE | ffi::SQLITE_OPEN_URI;
84        let connection_status = unsafe {
85            ffi::sqlite3_open_v2(database_url.as_ptr(), &mut conn_pointer, flags, ptr::null())
86        };
87
88        match connection_status {
89            ffi::SQLITE_OK => {
90                let conn_pointer = unsafe { NonNull::new_unchecked(conn_pointer) };
91                Ok(RawConnection {
92                    internal_connection: conn_pointer,
93                    commit_hook: None,
94                    rollback_hook: None,
95                    progress_hook: None,
96                })
97            }
98            err_code => {
99                let message = super::error_message(err_code);
100                // sqlite3_open_v2() may allocate a database connection handle
101                // even on failure. To avoid a resource leak, it must be released
102                // with sqlite3_close(). Passing a null pointer to sqlite3_close()
103                // is a harmless no-op, so no null check is needed.
104                // See: https://www.sqlite.org/c3ref/open.html
105                unsafe { ffi::sqlite3_close(conn_pointer) };
106                Err(ConnectionError::BadConnection(message.into()))
107            }
108        }
109    }
110
111    pub(super) fn exec(&self, query: &str) -> QueryResult<()> {
112        let query = CString::new(query)?;
113        let callback_fn = None;
114        let callback_arg = ptr::null_mut();
115        let result = unsafe {
116            ffi::sqlite3_exec(
117                self.internal_connection.as_ptr(),
118                query.as_ptr(),
119                callback_fn,
120                callback_arg,
121                ptr::null_mut(),
122            )
123        };
124
125        ensure_sqlite_ok(result, self.internal_connection.as_ptr())
126    }
127
128    pub(super) fn rows_affected_by_last_query(
129        &self,
130    ) -> Result<usize, Box<dyn core::error::Error + Send + Sync>> {
131        let r = unsafe { ffi::sqlite3_changes(self.internal_connection.as_ptr()) };
132
133        Ok(r.try_into()?)
134    }
135
136    pub(super) fn last_insert_rowid(&self) -> i64 {
137        unsafe { ffi::sqlite3_last_insert_rowid(self.internal_connection.as_ptr()) }
138    }
139
140    pub(super) fn register_sql_function<F, Ret, RetSqlType>(
141        &self,
142        fn_name: &str,
143        num_args: usize,
144        behavior: SqliteFunctionBehavior,
145        f: F,
146    ) -> QueryResult<()>
147    where
148        F: FnMut(&Self, &mut [*mut ffi::sqlite3_value]) -> QueryResult<Ret>
149            + core::panic::UnwindSafe
150            + Send
151            + 'static,
152        Ret: ToSql<RetSqlType, Sqlite>,
153        Sqlite: HasSqlType<RetSqlType>,
154    {
155        let c_fn_name = Self::get_fn_name(fn_name)?;
156        let flags = behavior.to_flags();
157        let num_args = num_args
158            .try_into()
159            .map_err(|e| Error::SerializationError(Box::new(e)))?;
160        // only create the pointer as last step here
161        // as we can otherwise leak memory
162        let callback_fn = Box::into_raw(Box::new(CustomFunctionUserPtr {
163            callback: f,
164            function_name: fn_name.to_owned(),
165        }));
166
167        let result = unsafe {
168            ffi::sqlite3_create_function_v2(
169                self.internal_connection.as_ptr(),
170                c_fn_name.as_ptr(),
171                num_args,
172                flags,
173                callback_fn as *mut _,
174                Some(run_custom_function::<F, Ret, RetSqlType>),
175                None,
176                None,
177                Some(destroy_boxed::<CustomFunctionUserPtr<F>>),
178            )
179        };
180
181        Self::process_sql_function_result(result)
182    }
183
184    pub(super) fn register_aggregate_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
185        &self,
186        fn_name: &str,
187        num_args: usize,
188        behavior: SqliteFunctionBehavior,
189    ) -> QueryResult<()>
190    where
191        A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send + core::panic::UnwindSafe,
192        Args: FromSqlRow<ArgsSqlType, Sqlite>,
193        Ret: ToSql<RetSqlType, Sqlite>,
194        Sqlite: HasSqlType<RetSqlType>,
195    {
196        let fn_name = Self::get_fn_name(fn_name)?;
197        let flags = behavior.to_flags();
198        let num_args = num_args
199            .try_into()
200            .map_err(|e| Error::SerializationError(Box::new(e)))?;
201
202        let result = unsafe {
203            ffi::sqlite3_create_function_v2(
204                self.internal_connection.as_ptr(),
205                fn_name.as_ptr(),
206                num_args,
207                flags,
208                core::ptr::null_mut(),
209                None,
210                Some(run_aggregator_step_function::<_, _, _, _, A>),
211                Some(run_aggregator_final_function::<_, _, _, _, A>),
212                None,
213            )
214        };
215
216        Self::process_sql_function_result(result)
217    }
218
219    pub(super) fn register_collation_function<F>(
220        &self,
221        collation_name: &str,
222        collation: F,
223    ) -> QueryResult<()>
224    where
225        F: Fn(&str, &str) -> core::cmp::Ordering + core::panic::UnwindSafe + Send + 'static,
226    {
227        let c_collation_name = Self::get_fn_name(collation_name)?;
228        // only create the pointer as last step here as we otherwise could leak memory
229        let callback_fn = Box::into_raw(Box::new(CollationUserPtr {
230            callback: collation,
231            collation_name: collation_name.to_owned(),
232        }));
233
234        let result = unsafe {
235            ffi::sqlite3_create_collation_v2(
236                self.internal_connection.as_ptr(),
237                c_collation_name.as_ptr(),
238                ffi::SQLITE_UTF8,
239                callback_fn as *mut _,
240                Some(run_collation_function::<F>),
241                Some(destroy_boxed::<CollationUserPtr<F>>),
242            )
243        };
244
245        let result = Self::process_sql_function_result(result);
246        if result.is_err() {
247            destroy_boxed::<CollationUserPtr<F>>(callback_fn as *mut _);
248        }
249        result
250    }
251
252    pub(super) fn serialize(&mut self) -> SerializedDatabase {
253        unsafe {
254            let mut size: ffi::sqlite3_int64 = 0;
255            let data_ptr = ffi::sqlite3_serialize(
256                self.internal_connection.as_ptr(),
257                core::ptr::null(),
258                &mut size as *mut _,
259                0,
260            );
261            SerializedDatabase::new(
262                data_ptr,
263                size.try_into()
264                    .expect("Cannot fit the serialized database into memory"),
265            )
266        }
267    }
268
269    // SAFETY:
270    // Any caller must ensure that the provided data buffer is valid and not modified until the database connection is closed
271    // Sqlite's documentation states:
272    // Applications must not modify the buffer P or invalidate it before the database connection D is closed.
273    pub(super) unsafe fn deserialize(&mut self, data: &[u8]) -> QueryResult<()> {
274        let db_size = data
275            .len()
276            .try_into()
277            .map_err(|e| Error::DeserializationError(Box::new(e)))?;
278        // the cast for `ffi::SQLITE_DESERIALIZE_READONLY` is required for old libsqlite3-sys versions
279        #[allow(clippy::unnecessary_cast)]
280        unsafe {
281            let result = ffi::sqlite3_deserialize(
282                self.internal_connection.as_ptr(),
283                core::ptr::null(),
284                data.as_ptr() as *mut u8,
285                db_size,
286                db_size,
287                ffi::SQLITE_DESERIALIZE_READONLY as u32,
288            );
289
290            ensure_sqlite_ok(result, self.internal_connection.as_ptr())
291        }
292    }
293
294    pub(super) fn set_limit(&self, limit: SqliteLimit, value: i32) -> i32 {
295        unsafe { ffi::sqlite3_limit(self.internal_connection.as_ptr(), limit.to_ffi(), value) }
296    }
297
298    pub(super) fn get_limit(&self, limit: SqliteLimit) -> i32 {
299        unsafe {
300            // Passing -1 queries the current value without changing it
301            ffi::sqlite3_limit(self.internal_connection.as_ptr(), limit.to_ffi(), -1)
302        }
303    }
304
305    /// Set a boolean db_config option.
306    pub(super) fn set_db_config_bool(&self, op: i32, value: bool) -> QueryResult<()> {
307        let mut result_value: libc::c_int = 0;
308        let new_value: libc::c_int = if value { 1 } else { 0 };
309
310        let result = unsafe {
311            ffi::sqlite3_db_config(
312                self.internal_connection.as_ptr(),
313                op,
314                new_value,
315                &mut result_value as *mut libc::c_int,
316            )
317        };
318
319        ensure_sqlite_ok(result, self.internal_connection.as_ptr())
320    }
321
322    /// Get a boolean db_config option.
323    pub(super) fn get_db_config_bool(&self, op: i32) -> QueryResult<bool> {
324        let mut current_value: libc::c_int = 0;
325
326        let result = unsafe {
327            ffi::sqlite3_db_config(
328                self.internal_connection.as_ptr(),
329                op,
330                -1_i32, // -1 queries without changing
331                &mut current_value as *mut libc::c_int,
332            )
333        };
334
335        ensure_sqlite_ok(result, self.internal_connection.as_ptr())?;
336        Ok(current_value != 0)
337    }
338
339    fn get_fn_name(fn_name: &str) -> Result<CString, NulError> {
340        CString::new(fn_name)
341    }
342
343    fn process_sql_function_result(result: i32) -> Result<(), Error> {
344        if result == ffi::SQLITE_OK {
345            Ok(())
346        } else {
347            let error_message = super::error_message(result);
348            Err(DatabaseError(
349                DatabaseErrorKind::Unknown,
350                Box::new(error_message.to_string()),
351            ))
352        }
353    }
354
355    pub(super) fn blob_open<'conn>(
356        &'conn self,
357        database_name: &str,
358        table_name: &str,
359        column_name: &str,
360        row_id: i64,
361    ) -> Result<super::sqlite_blob::SqliteReadOnlyBlob<'conn>, Error> {
362        let database_name = alloc::ffi::CString::new(database_name)?;
363        let column_name = alloc::ffi::CString::new(column_name)?;
364        let table_name = alloc::ffi::CString::new(table_name)?;
365
366        let mut blob: *mut ffi::sqlite3_blob = core::ptr::null_mut();
367
368        // SAFETY: All variables are properly initialized
369        let ret = unsafe {
370            ffi::sqlite3_blob_open(
371                self.internal_connection.as_ptr(),
372                database_name.as_c_str().as_ptr(),
373                table_name.as_c_str().as_ptr(),
374                column_name.as_c_str().as_ptr(),
375                row_id,
376                0,
377                &mut blob,
378            )
379        };
380
381        Self::process_sql_function_result(ret)?;
382
383        // SAFETY: `sqlite3_blob_open` initializes the `blob` variable IF the return value:
384        //
385        // > On success, SQLITE_OK is returned and the new BLOB handle is stored in *ppBlob.
386        // > Otherwise an error code is returned and, unless the error code is SQLITE_MISUSE,
387        // > *ppBlob is set to NULL.
388        //
389        // And we checked the `ret` value above
390        let blob = unsafe { core::ptr::NonNull::new_unchecked(blob) };
391
392        // SAFETY: According to the SQLite docs, this can only fail if an invalid pointer is passed
393        let blob_size = unsafe { ffi::sqlite3_blob_bytes(blob.as_ptr()) };
394        let blob_size = usize::try_from(blob_size).map_err(Error::IntegerConversion)?;
395
396        Ok(super::sqlite_blob::SqliteReadOnlyBlob {
397            blob,
398            read_index: 0,
399            blob_size,
400            _pd: core::marker::PhantomData,
401        })
402    }
403
404    /// Sets the commit hook, replacing any previous one.
405    ///
406    /// # Safety
407    ///
408    /// The `ptr` is derived from `&raw mut *boxed` and points to the heap
409    /// allocation of the closure. The `commit_hook_trampoline` function
410    /// matches the signature expected by `sqlite3_commit_hook`. The pointer
411    /// remains valid because we store `boxed` in `self.commit_hook` below,
412    /// keeping it alive for the lifetime of this `RawConnection`.
413    pub(super) fn set_commit_hook<F>(&mut self, hook: F)
414    where
415        F: FnMut() -> CommitDecision + Send + 'static,
416    {
417        let mut boxed: Box<dyn FnMut() -> CommitDecision + Send> = Box::new(hook);
418        let ptr = &raw mut *boxed as *mut libc::c_void;
419
420        unsafe {
421            ffi::sqlite3_commit_hook(
422                self.internal_connection.as_ptr(),
423                Some(commit_hook_trampoline::<F>),
424                ptr,
425            );
426        }
427
428        // The old Box (if any) is dropped here after SQLite has already
429        // switched to the new callback, preventing use-after-free.
430        self.commit_hook = Some(boxed);
431    }
432
433    /// Removes the commit hook.
434    ///
435    /// # Safety
436    ///
437    /// `self.internal_connection` is a valid pointer to an open SQLite
438    /// connection. Passing `None` as the hook and `null_mut()` as the user
439    /// data unregisters any existing commit hook. The hook is unregistered
440    /// before dropping `self.commit_hook` to prevent callbacks from firing
441    /// during cleanup.
442    pub(super) fn remove_commit_hook(&mut self) {
443        unsafe {
444            ffi::sqlite3_commit_hook(self.internal_connection.as_ptr(), None, ptr::null_mut());
445        }
446        self.commit_hook = None;
447    }
448
449    /// Sets the rollback hook, replacing any previous one.
450    ///
451    /// # Safety
452    ///
453    /// The `ptr` is derived from `&raw mut *boxed` and points to the heap
454    /// allocation of the closure. The `rollback_hook_trampoline` function
455    /// matches the signature expected by `sqlite3_rollback_hook`. The pointer
456    /// remains valid because we store `boxed` in `self.rollback_hook` below,
457    /// keeping it alive for the lifetime of this `RawConnection`.
458    pub(super) fn set_rollback_hook<F>(&mut self, hook: F)
459    where
460        F: FnMut() + Send + 'static,
461    {
462        let mut boxed: Box<dyn FnMut() + Send> = Box::new(hook);
463        let ptr = &raw mut *boxed as *mut libc::c_void;
464
465        unsafe {
466            ffi::sqlite3_rollback_hook(
467                self.internal_connection.as_ptr(),
468                Some(rollback_hook_trampoline::<F>),
469                ptr,
470            );
471        }
472
473        // The old Box (if any) is dropped here after SQLite has already
474        // switched to the new callback, preventing use-after-free.
475        self.rollback_hook = Some(boxed);
476    }
477
478    /// Removes the rollback hook.
479    ///
480    /// # Safety
481    ///
482    /// `self.internal_connection` is a valid pointer to an open SQLite
483    /// connection. Passing `None` as the hook and `null_mut()` as the user
484    /// data unregisters any existing rollback hook. The hook is unregistered
485    /// before dropping `self.rollback_hook` to prevent callbacks from firing
486    /// during cleanup.
487    pub(super) fn remove_rollback_hook(&mut self) {
488        unsafe {
489            ffi::sqlite3_rollback_hook(self.internal_connection.as_ptr(), None, ptr::null_mut());
490        }
491        self.rollback_hook = None;
492    }
493
494    /// Sets the progress handler, replacing any previous one.
495    ///
496    /// `n` is the approximate number of VM instructions between callbacks.
497    ///
498    /// # Safety
499    ///
500    /// The `ptr` is derived from `&raw mut *boxed` and points to the heap
501    /// allocation of the closure. The `progress_handler_trampoline` function
502    /// matches the signature expected by `sqlite3_progress_handler`. The
503    /// pointer remains valid because we store `boxed` in `self.progress_hook`
504    /// below, keeping it alive for the lifetime of this `RawConnection`.
505    pub(super) fn set_progress_handler<F>(&mut self, n: NonZeroU32, hook: F)
506    where
507        F: FnMut() -> ProgressDecision + Send + 'static,
508    {
509        let mut boxed: Box<dyn FnMut() -> ProgressDecision + Send> = Box::new(hook);
510        let ptr = &raw mut *boxed as *mut libc::c_void;
511
512        // `sqlite3_progress_handler` takes a c_int. A value above `i32::MAX`
513        // would wrap to a non-positive number and disable the handler, so clamp
514        // it to `i32::MAX` instead.
515        let n = i32::try_from(n.get()).unwrap_or(i32::MAX);
516
517        unsafe {
518            ffi::sqlite3_progress_handler(
519                self.internal_connection.as_ptr(),
520                n,
521                Some(progress_handler_trampoline::<F>),
522                ptr,
523            );
524        }
525
526        // The old Box (if any) is dropped here after SQLite has already
527        // switched to the new callback, preventing use-after-free.
528        self.progress_hook = Some(boxed);
529    }
530
531    /// Removes the progress handler.
532    ///
533    /// # Safety
534    ///
535    /// `self.internal_connection` is a valid pointer to an open SQLite
536    /// connection. Passing `None` as the handler and `null_mut()` as the user
537    /// data unregisters any existing progress handler. The handler is
538    /// unregistered before dropping `self.progress_hook` to prevent callbacks
539    /// from firing during cleanup.
540    pub(super) fn remove_progress_handler(&mut self) {
541        unsafe {
542            ffi::sqlite3_progress_handler(
543                self.internal_connection.as_ptr(),
544                0,
545                None,
546                ptr::null_mut(),
547            );
548        }
549        self.progress_hook = None;
550    }
551}
552
553impl Drop for RawConnection {
554    fn drop(&mut self) {
555        use crate::util::std_compat::panicking;
556
557        // Unregister before close so the boxed closure drops before sqlite3_close.
558        self.remove_commit_hook();
559        self.remove_rollback_hook();
560        self.remove_progress_handler();
561
562        let close_result = unsafe { ffi::sqlite3_close(self.internal_connection.as_ptr()) };
563        if close_result != ffi::SQLITE_OK {
564            let error_message = super::error_message(close_result);
565            if panicking() {
566                #[cfg(feature = "std")]
567                {
    ::std::io::_eprint(format_args!("Error closing SQLite connection: {0}\n",
            error_message));
};eprintln!("Error closing SQLite connection: {error_message}");
568            } else {
569                {
    ::core::panicking::panic_fmt(format_args!("Error closing SQLite connection: {0}",
            error_message));
};panic!("Error closing SQLite connection: {error_message}");
570            }
571        }
572    }
573}
574
575enum SqliteCallbackError {
576    Abort(&'static str),
577    DieselError(crate::result::Error),
578    Panic(String),
579}
580
581impl SqliteCallbackError {
582    fn emit(&self, ctx: *mut ffi::sqlite3_context) {
583        let s;
584        let msg = match self {
585            SqliteCallbackError::Abort(msg) => *msg,
586            SqliteCallbackError::DieselError(e) => {
587                s = e.to_string();
588                &s
589            }
590            SqliteCallbackError::Panic(msg) => msg,
591        };
592        unsafe {
593            context_error_str(ctx, msg);
594        }
595    }
596}
597
598impl From<crate::result::Error> for SqliteCallbackError {
599    fn from(e: crate::result::Error) -> Self {
600        Self::DieselError(e)
601    }
602}
603
604struct CustomFunctionUserPtr<F> {
605    callback: F,
606    function_name: String,
607}
608
609#[allow(warnings)]
610extern "C" fn run_custom_function<F, Ret, RetSqlType>(
611    ctx: *mut ffi::sqlite3_context,
612    num_args: libc::c_int,
613    value_ptr: *mut *mut ffi::sqlite3_value,
614) where
615    F: FnMut(&RawConnection, &mut [*mut ffi::sqlite3_value]) -> QueryResult<Ret>
616        + core::panic::UnwindSafe
617        + Send
618        + 'static,
619    Ret: ToSql<RetSqlType, Sqlite>,
620    Sqlite: HasSqlType<RetSqlType>,
621{
622    use core::ops::Deref;
623    static NULL_DATA_ERR: &str = "An unknown error occurred. sqlite3_user_data returned a null pointer. This should never happen.";
624    static NULL_CONN_ERR: &str = "An unknown error occurred. sqlite3_context_db_handle returned a null pointer. This should never happen.";
625
626    let conn = match unsafe { NonNull::new(ffi::sqlite3_context_db_handle(ctx)) } {
627        // We use `ManuallyDrop` here because we do not want to run the
628        // Drop impl of `RawConnection` as this would close the connection
629        Some(conn) => mem::ManuallyDrop::new(RawConnection::from_ptr(conn)),
630        None => {
631            unsafe { context_error_str(ctx, NULL_CONN_ERR) };
632            return;
633        }
634    };
635
636    let data_ptr = unsafe { ffi::sqlite3_user_data(ctx) };
637
638    let mut data_ptr = match NonNull::new(data_ptr as *mut CustomFunctionUserPtr<F>) {
639        None => unsafe {
640            context_error_str(ctx, NULL_DATA_ERR);
641            return;
642        },
643        Some(mut f) => f,
644    };
645    let data_ptr = unsafe { data_ptr.as_mut() };
646
647    // We need this to move the reference into the catch_unwind part
648    // this is sound as `F` itself and the stored string is `UnwindSafe`
649    let callback = core::panic::AssertUnwindSafe(&mut data_ptr.callback);
650    // conn holds a `Box<dyn FnMut() -> CommitDecision + Send>` hook field which is not UnwindSafe.
651    // The ManuallyDrop wrapper ensures we never run RawConnection's Drop.
652    let conn = core::panic::AssertUnwindSafe(conn);
653
654    let result = crate::util::std_compat::catch_unwind(move || {
655        let _ = &callback;
656        let args = unsafe { slice::from_raw_parts_mut(value_ptr, num_args as _) };
657        let res = (callback.0)(&*conn, args)?;
658        let value = process_sql_function_result(&res)?;
659        // We've checked already that ctx is not null
660        unsafe {
661            value.result_of(&mut *ctx);
662        }
663        Ok(())
664    })
665    .unwrap_or_else(|p| Err(SqliteCallbackError::Panic(data_ptr.function_name.clone())));
666    if let Err(e) = result {
667        e.emit(ctx);
668    }
669}
670
671#[allow(warnings)]
672extern "C" fn run_aggregator_step_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
673    ctx: *mut ffi::sqlite3_context,
674    num_args: libc::c_int,
675    value_ptr: *mut *mut ffi::sqlite3_value,
676) where
677    A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send + core::panic::UnwindSafe,
678    Args: FromSqlRow<ArgsSqlType, Sqlite>,
679    Ret: ToSql<RetSqlType, Sqlite>,
680    Sqlite: HasSqlType<RetSqlType>,
681{
682    let result = crate::util::std_compat::catch_unwind(move || {
683        let args = unsafe { slice::from_raw_parts_mut(value_ptr, num_args as _) };
684        run_aggregator_step::<A, Args, ArgsSqlType>(ctx, args)
685    })
686    .unwrap_or_else(|e| {
687        Err(SqliteCallbackError::Panic(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}::step() panicked",
                core::any::type_name::<A>()))
    })alloc::format!(
688            "{}::step() panicked",
689            core::any::type_name::<A>()
690        )))
691    });
692
693    match result {
694        Ok(()) => {}
695        Err(e) => e.emit(ctx),
696    }
697}
698
699fn run_aggregator_step<A, Args, ArgsSqlType>(
700    ctx: *mut ffi::sqlite3_context,
701    args: &mut [*mut ffi::sqlite3_value],
702) -> Result<(), SqliteCallbackError>
703where
704    A: SqliteAggregateFunction<Args>,
705    Args: FromSqlRow<ArgsSqlType, Sqlite>,
706{
707    let aggregator = unsafe {
708        const {
709            if core::mem::size_of::<*mut A>() == 0 {
710                {
    ::core::panicking::panic_fmt(format_args!("The pointer size is zero, that\'s unexpected.If you ever see this error message open a issuedescribing your environment"));
};panic!(
711                    "The pointer size is zero, that's unexpected.\
712                        If you ever see this error message open a issue\
713                        describing your environment"
714                );
715            }
716        }
717        // sqlite3_aggregate_context will return a memory allocation of the requested
718        // size. For the first call this will be zeroed, for any future call in the same execution
719        // this will contain the value we wrote into it.
720        //
721        // We write just a pointer to rust allocated memory in there to
722        // have the rust side deal with layout and alignment of our aggregator
723        let ctx = ffi::sqlite3_aggregate_context(
724            ctx,
725            core::mem::size_of::<*mut A>()
726                .try_into()
727                .expect("Memory size of a pointer is smaller than i32::MAX"),
728        )
729        // we cast the returned memory here to be a pointer to the aggregate instance
730        .cast::<*mut A>();
731        // we are interested in the inner pointer
732        let inner = &mut *ctx;
733        // if the inner pointer is null we the aggregate_step
734        // function is executed the first time and we need to create the actual
735        // aggregator
736        if inner.is_null() {
737            // for that we allocate a box and turn it into a raw pointer
738            // by leaking the memory
739            let obj = Box::into_raw(Box::new(A::default()));
740            *inner = obj;
741        }
742        // at this point the inner value is never null
743        // as we initialised in in the null branch above,
744        // therefore it's sound to dereference the pointer here
745        &mut **inner
746    };
747
748    let args = build_sql_function_args::<ArgsSqlType, Args>(args)?;
749
750    aggregator.step(args);
751    Ok(())
752}
753
754extern "C" fn run_aggregator_final_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
755    ctx: *mut ffi::sqlite3_context,
756) where
757    A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send,
758    Args: FromSqlRow<ArgsSqlType, Sqlite>,
759    Ret: ToSql<RetSqlType, Sqlite>,
760    Sqlite: HasSqlType<RetSqlType>,
761{
762    let result = crate::util::std_compat::catch_unwind(|| {
763        let aggregator = unsafe {
764            // Get back the aggregated context
765            // This might be null
766            let ctx = ffi::sqlite3_aggregate_context(
767                ctx,
768                // use zero sized allocation here to not allocate if this is the first call to `sqlite3_aggregate_context`
769                0,
770            )
771            // the allocation contains a pointer to the actual aggregator
772            .cast::<*mut A>();
773            // if the context was not allocated yet
774            // we get back a null pointer here due to
775            // the requested zero sized allocation
776            if ctx.is_null() {
777                None
778            } else {
779                // from this point we are interested in the inner pointer
780                // we checked above that this pointer is not null
781                // so it's sound to dereference it
782                let inner = &mut *ctx;
783                if inner.is_null() {
784                    // if the inner pointer is null the aggregator has not been initialized
785                    None
786                } else {
787                    // if it's not null
788                    // we need to construct back the box and move out the
789                    // value to correctly deallocate the allocation
790                    let value = Box::from_raw(*inner);
791                    let value = Some(*value);
792                    // we also want to write a null pointer back to the
793                    // context to make sure that there is no dangling pointer left
794                    *inner = core::ptr::null_mut();
795                    value
796                }
797            }
798        };
799
800        let res = A::finalize(aggregator);
801        let value = process_sql_function_result(&res)?;
802        // We've checked already that ctx is not null
803        let r = unsafe { value.result_of(&mut *ctx) };
804        r.map_err(|e| {
805            SqliteCallbackError::DieselError(crate::result::Error::SerializationError(Box::new(e)))
806        })?;
807        Ok(())
808    })
809    .unwrap_or_else(|_e| {
810        Err(SqliteCallbackError::Panic(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}::finalize() panicked",
                core::any::type_name::<A>()))
    })alloc::format!(
811            "{}::finalize() panicked",
812            core::any::type_name::<A>()
813        )))
814    });
815    if let Err(e) = result {
816        e.emit(ctx);
817    }
818}
819
820unsafe fn context_error_str(ctx: *mut ffi::sqlite3_context, error: &str) {
821    let len: i32 = error.len().try_into().unwrap_or(i32::MAX);
822    unsafe {
823        ffi::sqlite3_result_error(ctx, error.as_ptr() as *const _, len);
824    }
825}
826
827struct CollationUserPtr<F> {
828    callback: F,
829    collation_name: String,
830}
831
832#[allow(warnings)]
833extern "C" fn run_collation_function<F>(
834    user_ptr: *mut libc::c_void,
835    lhs_len: libc::c_int,
836    lhs_ptr: *const libc::c_void,
837    rhs_len: libc::c_int,
838    rhs_ptr: *const libc::c_void,
839) -> libc::c_int
840where
841    F: Fn(&str, &str) -> core::cmp::Ordering + Send + core::panic::UnwindSafe + 'static,
842{
843    let user_ptr = user_ptr as *const CollationUserPtr<F>;
844    let user_ptr = core::panic::AssertUnwindSafe(unsafe { user_ptr.as_ref() });
845
846    let result = crate::util::std_compat::catch_unwind(|| {
847        let user_ptr = user_ptr.ok_or_else(|| {
848            SqliteCallbackError::Abort(
849                "Got a null pointer as data pointer. This should never happen",
850            )
851        })?;
852        for (ptr, len, side) in &[(rhs_ptr, rhs_len, "rhs"), (lhs_ptr, lhs_len, "lhs")] {
853            if *len < 0 {
854                {
    ::std::io::_eprint(format_args!("An unknown error occurred. {0}_len is negative. This should never happen.If you see this message, please open an issue at https://github.com/diesel-rs/diesel/issues/new.\nSource location: {1}:{2}\n",
            side, "diesel/src/sqlite/connection/raw.rs", 854u32));
};
crate::util::std_compat::abort();assert_fail!(
855                    "An unknown error occurred. {}_len is negative. This should never happen.",
856                    side
857                );
858            }
859            if ptr.is_null() {
860                {
    ::std::io::_eprint(format_args!("An unknown error occurred. {0}_ptr is a null pointer. This should never happen.If you see this message, please open an issue at https://github.com/diesel-rs/diesel/issues/new.\nSource location: {1}:{2}\n",
            side, "diesel/src/sqlite/connection/raw.rs", 860u32));
};
crate::util::std_compat::abort();assert_fail!(
861                "An unknown error occurred. {}_ptr is a null pointer. This should never happen.",
862                side
863            );
864            }
865        }
866
867        let (rhs, lhs) = unsafe {
868            // Depending on the eTextRep-parameter to sqlite3_create_collation_v2() the strings can
869            // have various encodings. register_collation_function() always selects SQLITE_UTF8, so the
870            // pointers point to valid UTF-8 strings (assuming correct behavior of libsqlite3).
871            (
872                str::from_utf8(slice::from_raw_parts(rhs_ptr as *const u8, rhs_len as _)),
873                str::from_utf8(slice::from_raw_parts(lhs_ptr as *const u8, lhs_len as _)),
874            )
875        };
876
877        let rhs =
878            rhs.map_err(|_| SqliteCallbackError::Abort("Got an invalid UTF-8 string for rhs"))?;
879        let lhs =
880            lhs.map_err(|_| SqliteCallbackError::Abort("Got an invalid UTF-8 string for lhs"))?;
881
882        Ok((user_ptr.callback)(rhs, lhs))
883    })
884    .unwrap_or_else(|p| {
885        Err(SqliteCallbackError::Panic(
886            user_ptr
887                .map(|u| u.collation_name.clone())
888                .unwrap_or_default(),
889        ))
890    });
891
892    match result {
893        Ok(core::cmp::Ordering::Less) => -1,
894        Ok(core::cmp::Ordering::Equal) => 0,
895        Ok(core::cmp::Ordering::Greater) => 1,
896        Err(SqliteCallbackError::Abort(a)) => {
897            #[cfg(feature = "std")]
898            {
    ::std::io::_eprint(format_args!("Collation function {0} failed with: {1}\n",
            user_ptr.map(|c| &c.collation_name as &str).unwrap_or_default(),
            a));
};eprintln!(
899                "Collation function {} failed with: {}",
900                user_ptr
901                    .map(|c| &c.collation_name as &str)
902                    .unwrap_or_default(),
903                a
904            );
905            crate::util::std_compat::abort()
906        }
907        Err(SqliteCallbackError::DieselError(e)) => {
908            #[cfg(feature = "std")]
909            {
    ::std::io::_eprint(format_args!("Collation function {0} failed with: {1}\n",
            user_ptr.map(|c| &c.collation_name as &str).unwrap_or_default(),
            e));
};eprintln!(
910                "Collation function {} failed with: {}",
911                user_ptr
912                    .map(|c| &c.collation_name as &str)
913                    .unwrap_or_default(),
914                e
915            );
916            crate::util::std_compat::abort()
917        }
918        Err(SqliteCallbackError::Panic(msg)) => {
919            #[cfg(feature = "std")]
920            {
    ::std::io::_eprint(format_args!("Collation function {0} panicked\n",
            msg));
};eprintln!("Collation function {} panicked", msg);
921            crate::util::std_compat::abort()
922        }
923    }
924}
925
926extern "C" fn destroy_boxed<F>(data: *mut libc::c_void) {
927    let ptr = data as *mut F;
928    unsafe { core::mem::drop(Box::from_raw(ptr)) };
929}
930
931/// C trampoline for `sqlite3_commit_hook`.
932///
933/// # Safety
934///
935/// `user_data` must point to a live `F` stored in `RawConnection::commit_hook`.
936unsafe extern "C" fn commit_hook_trampoline<F>(user_data: *mut libc::c_void) -> libc::c_int
937where
938    F: FnMut() -> CommitDecision,
939{
940    let result = crate::util::std_compat::catch_unwind(core::panic::AssertUnwindSafe(|| {
941        // SAFETY: `user_data` points to a live `F` in `RawConnection::commit_hook`.
942        let f = unsafe { &mut *(user_data as *mut F) };
943        f()
944    }));
945
946    match result {
947        Ok(CommitDecision::Rollback) => 1,
948        Ok(CommitDecision::Proceed) => 0,
949        Err(_) => {
950            {
    ::std::io::_eprint(format_args!("Panic in sqlite3_commit_hook trampoline. If you see this message, please open an issue at https://github.com/diesel-rs/diesel/issues/new.\nSource location: {0}:{1}\n",
            "diesel/src/sqlite/connection/raw.rs", 950u32));
};
crate::util::std_compat::abort();assert_fail!("Panic in sqlite3_commit_hook trampoline. ");
951        }
952    }
953}
954
955/// C trampoline for `sqlite3_rollback_hook`.
956///
957/// # Safety
958///
959/// `user_data` must point to a live `F` stored in `RawConnection::rollback_hook`.
960unsafe extern "C" fn rollback_hook_trampoline<F>(user_data: *mut libc::c_void)
961where
962    F: FnMut(),
963{
964    let result = crate::util::std_compat::catch_unwind(core::panic::AssertUnwindSafe(|| {
965        // SAFETY: `user_data` points to a live `F` in `RawConnection::rollback_hook`.
966        let f = unsafe { &mut *(user_data as *mut F) };
967        f();
968    }));
969
970    if result.is_err() {
971        {
    ::std::io::_eprint(format_args!("Panic in sqlite3_rollback_hook trampoline. If you see this message, please open an issue at https://github.com/diesel-rs/diesel/issues/new.\nSource location: {0}:{1}\n",
            "diesel/src/sqlite/connection/raw.rs", 971u32));
};
crate::util::std_compat::abort();assert_fail!("Panic in sqlite3_rollback_hook trampoline. ");
972    }
973}
974
975/// C trampoline for `sqlite3_progress_handler`.
976///
977/// # Safety
978///
979/// `user_data` must point to a live `F` stored in `RawConnection::progress_hook`.
980unsafe extern "C" fn progress_handler_trampoline<F>(user_data: *mut libc::c_void) -> libc::c_int
981where
982    F: FnMut() -> ProgressDecision,
983{
984    let result = crate::util::std_compat::catch_unwind(core::panic::AssertUnwindSafe(|| {
985        // SAFETY: `user_data` points to a live `F` in `RawConnection::progress_hook`.
986        let f = unsafe { &mut *(user_data as *mut F) };
987        f()
988    }));
989
990    match result {
991        Ok(ProgressDecision::Interrupt) => 1,
992        Ok(ProgressDecision::Continue) => 0,
993        Err(_) => {
994            {
    ::std::io::_eprint(format_args!("Panic in sqlite3_progress_handler trampoline. If you see this message, please open an issue at https://github.com/diesel-rs/diesel/issues/new.\nSource location: {0}:{1}\n",
            "diesel/src/sqlite/connection/raw.rs", 994u32));
};
crate::util::std_compat::abort();assert_fail!("Panic in sqlite3_progress_handler trampoline. ");
995        }
996    }
997}