1#![allow(unsafe_code)] #[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::SqliteConnection;
9use super::authorizer::{AuthorizerContext, AuthorizerDecision};
10use super::collation_needed::{CollationNeededContext, SqliteTextRep};
11use super::functions::{build_sql_function_args, process_sql_function_result};
12use super::limits::SqliteLimit;
13use super::serialized_database::SerializedDatabase;
14use super::stmt::ensure_sqlite_ok;
15use super::trace::{SqliteTraceEvent, SqliteTraceFlags, TRACE_PROFILE, TRACE_ROW, TRACE_STMT};
16use super::update_hook::{SqliteChangeEvent, SqliteChangeOp};
17use super::{BusyDecision, CommitDecision, ProgressDecision};
18use super::{Sqlite, SqliteAggregateFunction};
19use crate::deserialize::FromSqlRow;
20use crate::result::Error::DatabaseError;
21use crate::result::*;
22use crate::serialize::ToSql;
23use crate::sql_types::HasSqlType;
24use crate::sqlite::{BorrowedSqliteConnection, SqliteFunctionBehavior};
25use alloc::borrow::{Cow, ToOwned};
26use alloc::boxed::Box;
27use alloc::ffi::{CString, NulError};
28use alloc::string::{String, ToString};
29use core::ffi as libc;
30use core::ffi::CStr;
31use core::num::NonZeroU32;
32use core::ptr::NonNull;
33use core::{mem, ptr, slice, str};
34
35pub(super) const SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE: i32 = 1020;
42pub(super) const SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE: i32 = 1021;
43
44pub(super) fn library_version_number() -> u32 {
46 let version = unsafe { ffi::sqlite3_libversion_number() };
49 version.cast_unsigned()
50}
51
52macro_rules! assert_fail {
61 ($fmt:expr_2021 $(,$args:tt)*) => {
62 #[cfg(feature = "std")]
63 eprint!(concat!(
64 $fmt,
65 "If you see this message, please open an issue at https://github.com/diesel-rs/diesel/issues/new.\n",
66 "Source location: {}:{}\n",
67 ), $($args,)* file!(), line!());
68 crate::util::std_compat::abort()
69 };
70}
71
72#[derive(#[automatically_derived]impl ::core::default::Default for Hooks {
#[inline]
fn default() -> Self {
Hooks {
update_hook: ::core::default::Default::default(),
commit_hook: ::core::default::Default::default(),
rollback_hook: ::core::default::Default::default(),
progress_hook: ::core::default::Default::default(),
wal_hook: ::core::default::Default::default(),
busy_handler: ::core::default::Default::default(),
authorizer_hook: ::core::default::Default::default(),
trace_hook: ::core::default::Default::default(),
collation_needed_hook: ::core::default::Default::default(),
}
}
}Default)]
79pub(super) struct Hooks {
80 update_hook: Option<Box<dyn FnMut(SqliteChangeEvent<'_>) + Send>>,
82 commit_hook: Option<Box<dyn FnMut() -> CommitDecision + Send>>,
84 rollback_hook: Option<Box<dyn FnMut() + Send>>,
86 progress_hook: Option<Box<dyn FnMut() -> ProgressDecision + Send>>,
88 wal_hook: Option<Box<dyn Fn(&'_ mut BorrowedSqliteConnection, &str, u32) + Send>>,
90 busy_handler: Option<Box<dyn FnMut(i32) -> BusyDecision + Send>>,
92 authorizer_hook: Option<Box<dyn FnMut(AuthorizerContext<'_>) -> AuthorizerDecision + Send>>,
94 trace_hook: Option<Box<dyn FnMut(SqliteTraceEvent<'_>) + Send>>,
96 collation_needed_hook:
98 Option<Box<dyn Fn(CollationNeededContext<'_>) -> Option<Collation> + Send>>,
99}
100
101#[allow(missing_debug_implementations, missing_copy_implementations)]
102pub(super) struct RawConnection {
103 pub(super) internal_connection: NonNull<ffi::sqlite3>,
104 hooks: Hooks,
105}
106
107type Collation = Box<dyn Fn(&str, &str) -> core::cmp::Ordering + Send + core::panic::UnwindSafe>;
108
109impl RawConnection {
110 pub(super) fn from_ptr(conn: NonNull<ffi::sqlite3>) -> Self {
113 RawConnection {
114 internal_connection: conn,
115 hooks: Hooks::default(),
116 }
117 }
118
119 pub(super) fn establish(database_url: &str) -> ConnectionResult<Self> {
120 let mut conn_pointer = ptr::null_mut();
121
122 let database_url = if database_url.starts_with("sqlite://") {
123 CString::new(database_url.replacen("sqlite://", "file:", 1))?
124 } else {
125 CString::new(database_url)?
126 };
127 let flags = ffi::SQLITE_OPEN_READWRITE | ffi::SQLITE_OPEN_CREATE | ffi::SQLITE_OPEN_URI;
128 let connection_status = unsafe {
129 ffi::sqlite3_open_v2(database_url.as_ptr(), &mut conn_pointer, flags, ptr::null())
130 };
131
132 match connection_status {
133 ffi::SQLITE_OK => {
134 let conn_pointer = unsafe { NonNull::new_unchecked(conn_pointer) };
135 Ok(RawConnection {
136 internal_connection: conn_pointer,
137 hooks: Hooks::default(),
138 })
139 }
140 err_code => {
141 let message = super::error_message(err_code);
142 unsafe { ffi::sqlite3_close(conn_pointer) };
148 Err(ConnectionError::BadConnection(message.into()))
149 }
150 }
151 }
152
153 pub(super) fn exec(&self, query: &str) -> QueryResult<()> {
154 let query = CString::new(query)?;
155 let callback_fn = None;
156 let callback_arg = ptr::null_mut();
157 let result = unsafe {
158 ffi::sqlite3_exec(
159 self.internal_connection.as_ptr(),
160 query.as_ptr(),
161 callback_fn,
162 callback_arg,
163 ptr::null_mut(),
164 )
165 };
166
167 ensure_sqlite_ok(result, self.internal_connection.as_ptr())
168 }
169
170 pub(super) fn rows_affected_by_last_query(
171 &self,
172 ) -> Result<usize, Box<dyn core::error::Error + Send + Sync>> {
173 let r = unsafe { ffi::sqlite3_changes(self.internal_connection.as_ptr()) };
174
175 Ok(r.try_into()?)
176 }
177
178 pub(super) fn last_insert_rowid(&self) -> i64 {
179 unsafe { ffi::sqlite3_last_insert_rowid(self.internal_connection.as_ptr()) }
180 }
181
182 pub(super) fn is_autocommit(&self) -> bool {
183 unsafe { ffi::sqlite3_get_autocommit(self.internal_connection.as_ptr()) != 0 }
185 }
186
187 pub(super) fn register_sql_function<F, Ret, RetSqlType>(
188 &self,
189 fn_name: &str,
190 num_args: usize,
191 behavior: SqliteFunctionBehavior,
192 f: F,
193 ) -> QueryResult<()>
194 where
195 F: FnMut(&Self, &mut [*mut ffi::sqlite3_value]) -> QueryResult<Ret>
196 + core::panic::UnwindSafe
197 + Send
198 + 'static,
199 Ret: ToSql<RetSqlType, Sqlite>,
200 Sqlite: HasSqlType<RetSqlType>,
201 {
202 let c_fn_name = Self::get_fn_name(fn_name)?;
203 let flags = behavior.to_flags();
204 let num_args = num_args
205 .try_into()
206 .map_err(|e| Error::SerializationError(Box::new(e)))?;
207 let callback_fn = Box::into_raw(Box::new(CustomFunctionUserPtr {
210 callback: f,
211 function_name: fn_name.to_owned(),
212 }));
213
214 let result = unsafe {
215 ffi::sqlite3_create_function_v2(
216 self.internal_connection.as_ptr(),
217 c_fn_name.as_ptr(),
218 num_args,
219 flags,
220 callback_fn as *mut _,
221 Some(run_custom_function::<F, Ret, RetSqlType>),
222 None,
223 None,
224 Some(destroy_boxed::<CustomFunctionUserPtr<F>>),
225 )
226 };
227
228 Self::process_sql_function_result(result)
229 }
230
231 pub(super) fn register_aggregate_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
232 &self,
233 fn_name: &str,
234 num_args: usize,
235 behavior: SqliteFunctionBehavior,
236 ) -> QueryResult<()>
237 where
238 A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send + core::panic::UnwindSafe,
239 Args: FromSqlRow<ArgsSqlType, Sqlite>,
240 Ret: ToSql<RetSqlType, Sqlite>,
241 Sqlite: HasSqlType<RetSqlType>,
242 {
243 let fn_name = Self::get_fn_name(fn_name)?;
244 let flags = behavior.to_flags();
245 let num_args = num_args
246 .try_into()
247 .map_err(|e| Error::SerializationError(Box::new(e)))?;
248
249 let result = unsafe {
250 ffi::sqlite3_create_function_v2(
251 self.internal_connection.as_ptr(),
252 fn_name.as_ptr(),
253 num_args,
254 flags,
255 core::ptr::null_mut(),
256 None,
257 Some(run_aggregator_step_function::<_, _, _, _, A>),
258 Some(run_aggregator_final_function::<_, _, _, _, A>),
259 None,
260 )
261 };
262
263 Self::process_sql_function_result(result)
264 }
265
266 pub(super) fn register_collation_function<F>(
267 &self,
268 collation_name: &str,
269 collation: F,
270 ) -> QueryResult<()>
271 where
272 F: Fn(&str, &str) -> core::cmp::Ordering + core::panic::UnwindSafe + Send + 'static,
273 {
274 let c_collation_name = Self::get_fn_name(collation_name)?;
275 let callback_fn = Box::into_raw(Box::new(CollationUserPtr {
277 callback: collation,
278 collation_name: collation_name.to_owned(),
279 }));
280
281 let result = unsafe {
282 ffi::sqlite3_create_collation_v2(
283 self.internal_connection.as_ptr(),
284 c_collation_name.as_ptr(),
285 ffi::SQLITE_UTF8,
286 callback_fn as *mut _,
287 Some(run_collation_function::<F>),
288 Some(destroy_boxed::<CollationUserPtr<F>>),
289 )
290 };
291
292 let result = Self::process_sql_function_result(result);
293 if result.is_err() {
294 destroy_boxed::<CollationUserPtr<F>>(callback_fn as *mut _);
295 }
296 result
297 }
298
299 pub(super) fn serialize(&mut self) -> SerializedDatabase {
300 let mut size: ffi::sqlite3_int64 = 0;
301 let data_ptr = unsafe {
303 ffi::sqlite3_serialize(
304 self.internal_connection.as_ptr(),
305 core::ptr::null(),
306 &mut size as *mut _,
307 0,
308 )
309 };
310 let data = match core::ptr::NonNull::new(data_ptr) {
314 Some(data) => data,
315 None if size == 0 => return SerializedDatabase::empty(),
316 None => return SerializedDatabase::allocation_failed(),
317 };
318 unsafe { SerializedDatabase::new(data, size) }
320 }
321
322 pub(super) fn deserialize(&mut self, data: &[u8]) -> QueryResult<()> {
325 let db_size = data
326 .len()
327 .try_into()
328 .map_err(|e| Error::DeserializationError(Box::new(e)))?;
329
330 let buffer = unsafe {
334 ffi::sqlite3_malloc64(data.len() as u64).cast::<u8>()
336 };
337 if buffer.is_null() && !data.is_empty() {
338 return Err(Error::DeserializationError(
339 "Failed to allocate the memory for the database buffer".into(),
340 ));
341 }
342 if !data.is_empty() {
343 unsafe {
344 core::ptr::copy_nonoverlapping(data.as_ptr(), buffer, data.len());
349 }
350 }
351
352 #[allow(clippy::unnecessary_cast)]
354 unsafe {
355 let result = ffi::sqlite3_deserialize(
356 self.internal_connection.as_ptr(),
357 core::ptr::null(),
358 buffer,
359 db_size,
360 db_size,
361 ffi::SQLITE_DESERIALIZE_READONLY as u32
362 | ffi::SQLITE_DESERIALIZE_FREEONCLOSE as u32,
363 );
364
365 ensure_sqlite_ok(result, self.internal_connection.as_ptr())
370 }
371 }
372
373 pub(super) fn set_limit(&self, limit: SqliteLimit, value: u32) -> u32 {
374 let value = i32::try_from(value).unwrap_or(i32::MAX);
375 let previous =
376 unsafe { ffi::sqlite3_limit(self.internal_connection.as_ptr(), limit.to_ffi(), value) };
377 u32::try_from(previous).unwrap_or(0)
379 }
380
381 pub(super) fn get_limit(&self, limit: SqliteLimit) -> u32 {
382 let current = unsafe {
383 ffi::sqlite3_limit(self.internal_connection.as_ptr(), limit.to_ffi(), -1)
385 };
386 u32::try_from(current).unwrap_or(0)
387 }
388
389 pub(super) fn set_db_config_bool(&self, op: i32, value: bool) -> QueryResult<()> {
391 let mut result_value: libc::c_int = 0;
392 let new_value: libc::c_int = if value { 1 } else { 0 };
393
394 let result = unsafe {
395 ffi::sqlite3_db_config(
396 self.internal_connection.as_ptr(),
397 op,
398 new_value,
399 &mut result_value as *mut libc::c_int,
400 )
401 };
402
403 ensure_sqlite_ok(result, self.internal_connection.as_ptr())
404 }
405
406 pub(super) fn get_db_config_bool(&self, op: i32) -> QueryResult<bool> {
408 let mut current_value: libc::c_int = 0;
409
410 let result = unsafe {
411 ffi::sqlite3_db_config(
412 self.internal_connection.as_ptr(),
413 op,
414 -1_i32, &mut current_value as *mut libc::c_int,
416 )
417 };
418
419 ensure_sqlite_ok(result, self.internal_connection.as_ptr())?;
420 Ok(current_value != 0)
421 }
422
423 fn get_fn_name(fn_name: &str) -> Result<CString, NulError> {
424 CString::new(fn_name)
425 }
426
427 fn process_sql_function_result(result: i32) -> Result<(), Error> {
428 if result == ffi::SQLITE_OK {
429 Ok(())
430 } else {
431 let error_message = super::error_message(result);
432 Err(DatabaseError(
433 DatabaseErrorKind::Unknown,
434 Box::new(error_message.to_string()),
435 ))
436 }
437 }
438
439 pub(super) fn blob_open<'conn>(
440 &'conn self,
441 database_name: &str,
442 table_name: &str,
443 column_name: &str,
444 row_id: i64,
445 ) -> Result<super::sqlite_blob::SqliteReadOnlyBlob<'conn>, Error> {
446 let database_name = alloc::ffi::CString::new(database_name)?;
447 let column_name = alloc::ffi::CString::new(column_name)?;
448 let table_name = alloc::ffi::CString::new(table_name)?;
449
450 let mut blob: *mut ffi::sqlite3_blob = core::ptr::null_mut();
451
452 let ret = unsafe {
454 ffi::sqlite3_blob_open(
455 self.internal_connection.as_ptr(),
456 database_name.as_c_str().as_ptr(),
457 table_name.as_c_str().as_ptr(),
458 column_name.as_c_str().as_ptr(),
459 row_id,
460 0,
461 &mut blob,
462 )
463 };
464
465 Self::process_sql_function_result(ret)?;
466
467 let blob = unsafe { core::ptr::NonNull::new_unchecked(blob) };
475
476 let blob_size = unsafe { ffi::sqlite3_blob_bytes(blob.as_ptr()) };
478 let blob_size = usize::try_from(blob_size).map_err(Error::IntegerConversion)?;
479
480 Ok(super::sqlite_blob::SqliteReadOnlyBlob {
481 blob: Some(blob),
482 read_index: 0,
483 blob_size,
484 _pd: core::marker::PhantomData,
485 })
486 }
487
488 pub(super) fn set_update_hook<F>(&mut self, hook: F)
500 where
501 F: FnMut(SqliteChangeEvent<'_>) + Send + 'static,
502 {
503 let mut boxed: Box<dyn FnMut(SqliteChangeEvent<'_>) + Send> = Box::new(hook);
504 let ptr = &raw mut *boxed as *mut libc::c_void;
505
506 unsafe {
507 ffi::sqlite3_update_hook(
508 self.internal_connection.as_ptr(),
509 Some(update_hook_trampoline::<F>),
510 ptr,
511 );
512 }
513
514 self.hooks.update_hook = Some(boxed);
517 }
518
519 pub(super) fn remove_update_hook(&mut self) {
528 unsafe {
529 ffi::sqlite3_update_hook(self.internal_connection.as_ptr(), None, ptr::null_mut());
530 }
531 self.hooks.update_hook = None;
532 }
533
534 pub(super) fn set_commit_hook<F>(&mut self, hook: F)
544 where
545 F: FnMut() -> CommitDecision + Send + 'static,
546 {
547 let mut boxed: Box<dyn FnMut() -> CommitDecision + Send> = Box::new(hook);
548 let ptr = &raw mut *boxed as *mut libc::c_void;
549
550 unsafe {
551 ffi::sqlite3_commit_hook(
552 self.internal_connection.as_ptr(),
553 Some(commit_hook_trampoline::<F>),
554 ptr,
555 );
556 }
557
558 self.hooks.commit_hook = Some(boxed);
561 }
562
563 pub(super) fn remove_commit_hook(&mut self) {
573 unsafe {
574 ffi::sqlite3_commit_hook(self.internal_connection.as_ptr(), None, ptr::null_mut());
575 }
576 self.hooks.commit_hook = None;
577 }
578
579 pub(super) fn set_rollback_hook<F>(&mut self, hook: F)
589 where
590 F: FnMut() + Send + 'static,
591 {
592 let mut boxed: Box<dyn FnMut() + Send> = Box::new(hook);
593 let ptr = &raw mut *boxed as *mut libc::c_void;
594
595 unsafe {
596 ffi::sqlite3_rollback_hook(
597 self.internal_connection.as_ptr(),
598 Some(rollback_hook_trampoline::<F>),
599 ptr,
600 );
601 }
602
603 self.hooks.rollback_hook = Some(boxed);
606 }
607
608 pub(super) fn remove_rollback_hook(&mut self) {
618 unsafe {
619 ffi::sqlite3_rollback_hook(self.internal_connection.as_ptr(), None, ptr::null_mut());
620 }
621 self.hooks.rollback_hook = None;
622 }
623
624 pub(super) fn set_progress_handler<F>(&mut self, n: NonZeroU32, hook: F)
636 where
637 F: FnMut() -> ProgressDecision + Send + 'static,
638 {
639 let mut boxed: Box<dyn FnMut() -> ProgressDecision + Send> = Box::new(hook);
640 let ptr = &raw mut *boxed as *mut libc::c_void;
641
642 let n = i32::try_from(n.get()).unwrap_or(i32::MAX);
646
647 unsafe {
648 ffi::sqlite3_progress_handler(
649 self.internal_connection.as_ptr(),
650 n,
651 Some(progress_handler_trampoline::<F>),
652 ptr,
653 );
654 }
655
656 self.hooks.progress_hook = Some(boxed);
659 }
660
661 pub(super) fn remove_progress_handler(&mut self) {
671 unsafe {
672 ffi::sqlite3_progress_handler(
673 self.internal_connection.as_ptr(),
674 0,
675 None,
676 ptr::null_mut(),
677 );
678 }
679 self.hooks.progress_hook = None;
680 }
681
682 pub(super) fn set_wal_hook<F>(&mut self, hook: F)
695 where
696 F: Fn(&'_ mut BorrowedSqliteConnection, &str, u32) + Send + 'static,
697 {
698 let boxed: Box<dyn Fn(&'_ mut BorrowedSqliteConnection, &str, u32) + Send> = Box::new(hook);
699 let ptr = &raw const *boxed as *mut libc::c_void;
700
701 unsafe {
702 ffi::sqlite3_wal_hook(
703 self.internal_connection.as_ptr(),
704 Some(wal_hook_trampoline::<F>),
705 ptr,
706 );
707 }
708
709 self.hooks.wal_hook = Some(boxed);
712 }
713
714 pub(super) fn remove_wal_hook(&mut self) {
724 unsafe {
725 ffi::sqlite3_wal_hook(self.internal_connection.as_ptr(), None, ptr::null_mut());
726 }
727 self.hooks.wal_hook = None;
728 }
729
730 pub(super) fn set_busy_handler<F>(&mut self, hook: F)
743 where
744 F: FnMut(i32) -> BusyDecision + Send + 'static,
745 {
746 let mut boxed: Box<dyn FnMut(i32) -> BusyDecision + Send> = Box::new(hook);
747 let ptr = &raw mut *boxed as *mut libc::c_void;
748
749 unsafe {
750 ffi::sqlite3_busy_handler(
751 self.internal_connection.as_ptr(),
752 Some(busy_handler_trampoline::<F>),
753 ptr,
754 );
755 }
756
757 self.hooks.busy_handler = Some(boxed);
760 }
761
762 pub(super) fn remove_busy_handler(&mut self) {
772 unsafe {
773 ffi::sqlite3_busy_handler(self.internal_connection.as_ptr(), None, ptr::null_mut());
774 }
775 self.hooks.busy_handler = None;
776 }
777
778 pub(super) fn set_busy_timeout(&mut self, timeout: core::time::Duration) {
790 let ms = i32::try_from(timeout.as_nanos().div_ceil(1_000_000)).unwrap_or(i32::MAX);
792 unsafe {
793 ffi::sqlite3_busy_timeout(self.internal_connection.as_ptr(), ms);
794 }
795 self.hooks.busy_handler = None;
796 }
797
798 pub(super) fn set_authorizer<F>(&mut self, hook: F)
809 where
810 F: FnMut(AuthorizerContext<'_>) -> AuthorizerDecision + Send + 'static,
811 {
812 let mut boxed: Box<dyn FnMut(AuthorizerContext<'_>) -> AuthorizerDecision + Send> =
813 Box::new(hook);
814 let ptr = &raw mut *boxed as *mut libc::c_void;
815
816 unsafe {
817 ffi::sqlite3_set_authorizer(
818 self.internal_connection.as_ptr(),
819 Some(authorizer_trampoline::<F>),
820 ptr,
821 );
822 }
823
824 self.hooks.authorizer_hook = Some(boxed);
827 }
828
829 pub(super) fn remove_authorizer(&mut self) {
839 unsafe {
840 ffi::sqlite3_set_authorizer(self.internal_connection.as_ptr(), None, ptr::null_mut());
841 }
842 self.hooks.authorizer_hook = None;
843 }
844
845 pub(super) fn set_trace<F>(&mut self, mask: SqliteTraceFlags, hook: F)
858 where
859 F: FnMut(SqliteTraceEvent<'_>) + Send + 'static,
860 {
861 let mut boxed: Box<dyn FnMut(SqliteTraceEvent<'_>) + Send> = Box::new(hook);
862 let ptr = &raw mut *boxed as *mut libc::c_void;
863
864 unsafe {
865 ffi::sqlite3_trace_v2(
866 self.internal_connection.as_ptr(),
867 SqliteTraceFlags::from_bits_truncate(mask.bits()).bits(),
869 Some(trace_trampoline::<F>),
870 ptr,
871 );
872 }
873
874 self.hooks.trace_hook = Some(boxed);
877 }
878
879 pub(super) fn remove_trace(&mut self) {
889 unsafe {
890 ffi::sqlite3_trace_v2(self.internal_connection.as_ptr(), 0, None, ptr::null_mut());
891 }
892 self.hooks.trace_hook = None;
893 }
894
895 pub(super) fn set_collation_needed_hook<F>(&mut self, hook: F)
903 where
904 F: Fn(CollationNeededContext<'_>) -> Option<Collation> + Send + 'static,
905 {
906 let boxed: Box<dyn Fn(CollationNeededContext<'_>) -> Option<Collation> + Send> =
907 Box::new(hook);
908 let ptr = &raw const *boxed as *mut libc::c_void;
909
910 unsafe {
911 ffi::sqlite3_collation_needed(
912 self.internal_connection.as_ptr(),
913 ptr,
914 Some(collation_needed_trampoline::<F>),
915 );
916 }
917
918 self.hooks.collation_needed_hook = Some(boxed);
921 }
922
923 pub(super) fn remove_collation_needed_hook(&mut self) {
931 unsafe {
932 ffi::sqlite3_collation_needed(self.internal_connection.as_ptr(), ptr::null_mut(), None);
933 }
934 self.hooks.collation_needed_hook = None;
935 }
936
937 pub(crate) fn reset_registered_hooks(&mut self) -> Hooks {
938 let Self {
940 internal_connection,
941 hooks,
942 } = self;
943 let db = internal_connection.as_ptr();
944 let hooks = core::mem::take(hooks);
945
946 unsafe {
951 if hooks.update_hook.is_some() {
952 ffi::sqlite3_update_hook(db, None, ptr::null_mut());
953 }
954 if hooks.commit_hook.is_some() {
955 ffi::sqlite3_commit_hook(db, None, ptr::null_mut());
956 }
957 if hooks.rollback_hook.is_some() {
958 ffi::sqlite3_rollback_hook(db, None, ptr::null_mut());
959 }
960 if hooks.progress_hook.is_some() {
961 ffi::sqlite3_progress_handler(db, 0, None, ptr::null_mut());
962 }
963 if hooks.wal_hook.is_some() {
964 ffi::sqlite3_wal_hook(db, None, ptr::null_mut());
965 }
966 if hooks.busy_handler.is_some() {
967 ffi::sqlite3_busy_handler(db, None, ptr::null_mut());
968 }
969 if hooks.authorizer_hook.is_some() {
970 ffi::sqlite3_set_authorizer(db, None, ptr::null_mut());
971 }
972 if hooks.trace_hook.is_some() {
973 ffi::sqlite3_trace_v2(db, 0, None, ptr::null_mut());
974 }
975 if hooks.collation_needed_hook.is_some() {
976 ffi::sqlite3_collation_needed(db, ptr::null_mut(), None);
977 }
978 }
979 hooks
980 }
981}
982
983impl Drop for RawConnection {
984 fn drop(&mut self) {
985 use crate::util::std_compat::panicking;
986
987 let _hooks = self.reset_registered_hooks();
990
991 let close_result = unsafe { ffi::sqlite3_close(self.internal_connection.as_ptr()) };
992 if close_result != ffi::SQLITE_OK {
993 let error_message = super::error_message(close_result);
994 if panicking() {
995 #[cfg(feature = "std")]
996 {
::std::io::_eprint(format_args!("Error closing SQLite connection: {0}\n",
error_message));
};eprintln!("Error closing SQLite connection: {error_message}");
997 } else {
998 {
::core::panicking::panic_fmt(format_args!("Error closing SQLite connection: {0}",
error_message));
};panic!("Error closing SQLite connection: {error_message}");
999 }
1000 }
1001 }
1002}
1003
1004enum SqliteCallbackError {
1005 Abort(&'static str),
1006 DieselError(crate::result::Error),
1007 Panic(String),
1008}
1009
1010impl SqliteCallbackError {
1011 fn emit(&self, ctx: *mut ffi::sqlite3_context) {
1012 let s;
1013 let msg = match self {
1014 SqliteCallbackError::Abort(msg) => *msg,
1015 SqliteCallbackError::DieselError(e) => {
1016 s = e.to_string();
1017 &s
1018 }
1019 SqliteCallbackError::Panic(msg) => msg,
1020 };
1021 unsafe {
1022 context_error_str(ctx, msg);
1023 }
1024 }
1025}
1026
1027impl From<crate::result::Error> for SqliteCallbackError {
1028 fn from(e: crate::result::Error) -> Self {
1029 Self::DieselError(e)
1030 }
1031}
1032
1033struct CustomFunctionUserPtr<F> {
1034 callback: F,
1035 function_name: String,
1036}
1037
1038#[allow(warnings)]
1039extern "C" fn run_custom_function<F, Ret, RetSqlType>(
1040 ctx: *mut ffi::sqlite3_context,
1041 num_args: libc::c_int,
1042 value_ptr: *mut *mut ffi::sqlite3_value,
1043) where
1044 F: FnMut(&RawConnection, &mut [*mut ffi::sqlite3_value]) -> QueryResult<Ret>
1045 + core::panic::UnwindSafe
1046 + Send
1047 + 'static,
1048 Ret: ToSql<RetSqlType, Sqlite>,
1049 Sqlite: HasSqlType<RetSqlType>,
1050{
1051 use core::ops::Deref;
1052 static NULL_DATA_ERR: &str = "An unknown error occurred. sqlite3_user_data returned a null pointer. This should never happen.";
1053 static NULL_CONN_ERR: &str = "An unknown error occurred. sqlite3_context_db_handle returned a null pointer. This should never happen.";
1054
1055 let conn = match unsafe { NonNull::new(ffi::sqlite3_context_db_handle(ctx)) } {
1056 Some(conn) => mem::ManuallyDrop::new(RawConnection::from_ptr(conn)),
1059 None => {
1060 unsafe { context_error_str(ctx, NULL_CONN_ERR) };
1061 return;
1062 }
1063 };
1064
1065 let data_ptr = unsafe { ffi::sqlite3_user_data(ctx) };
1066
1067 let mut data_ptr = match NonNull::new(data_ptr as *mut CustomFunctionUserPtr<F>) {
1068 None => unsafe {
1069 context_error_str(ctx, NULL_DATA_ERR);
1070 return;
1071 },
1072 Some(mut f) => f,
1073 };
1074 let data_ptr = unsafe { data_ptr.as_mut() };
1075
1076 let callback = core::panic::AssertUnwindSafe(&mut data_ptr.callback);
1079 let conn = core::panic::AssertUnwindSafe(conn);
1082
1083 let result = crate::util::std_compat::catch_unwind(move || {
1084 let _ = &callback;
1085 let args = unsafe { slice::from_raw_parts_mut(value_ptr, num_args as _) };
1086 let res = (callback.0)(&*conn, args)?;
1087 let value = process_sql_function_result(&res)?;
1088 unsafe {
1090 value.result_of(&mut *ctx);
1091 }
1092 Ok(())
1093 })
1094 .unwrap_or_else(|p| Err(SqliteCallbackError::Panic(data_ptr.function_name.clone())));
1095 if let Err(e) = result {
1096 e.emit(ctx);
1097 }
1098}
1099
1100#[allow(warnings)]
1101extern "C" fn run_aggregator_step_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
1102 ctx: *mut ffi::sqlite3_context,
1103 num_args: libc::c_int,
1104 value_ptr: *mut *mut ffi::sqlite3_value,
1105) where
1106 A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send + core::panic::UnwindSafe,
1107 Args: FromSqlRow<ArgsSqlType, Sqlite>,
1108 Ret: ToSql<RetSqlType, Sqlite>,
1109 Sqlite: HasSqlType<RetSqlType>,
1110{
1111 let result = crate::util::std_compat::catch_unwind(move || {
1112 let args = unsafe { slice::from_raw_parts_mut(value_ptr, num_args as _) };
1113 run_aggregator_step::<A, Args, ArgsSqlType>(ctx, args)
1114 })
1115 .unwrap_or_else(|e| {
1116 Err(SqliteCallbackError::Panic(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::step() panicked",
core::any::type_name::<A>()))
})alloc::format!(
1117 "{}::step() panicked",
1118 core::any::type_name::<A>()
1119 )))
1120 });
1121
1122 match result {
1123 Ok(()) => {}
1124 Err(e) => e.emit(ctx),
1125 }
1126}
1127
1128fn run_aggregator_step<A, Args, ArgsSqlType>(
1129 ctx: *mut ffi::sqlite3_context,
1130 args: &mut [*mut ffi::sqlite3_value],
1131) -> Result<(), SqliteCallbackError>
1132where
1133 A: SqliteAggregateFunction<Args>,
1134 Args: FromSqlRow<ArgsSqlType, Sqlite>,
1135{
1136 let aggregator = unsafe {
1137 const {
1138 if core::mem::size_of::<*mut A>() == 0 {
1139 {
::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!(
1140 "The pointer size is zero, that's unexpected.\
1141 If you ever see this error message open a issue\
1142 describing your environment"
1143 );
1144 }
1145 }
1146 let ctx = ffi::sqlite3_aggregate_context(
1153 ctx,
1154 core::mem::size_of::<*mut A>()
1155 .try_into()
1156 .expect("Memory size of a pointer is smaller than i32::MAX"),
1157 )
1158 .cast::<*mut A>();
1160 if ctx.is_null() {
1162 return Err(SqliteCallbackError::Abort(
1163 "sqlite3_aggregate_context failed to allocate memory for the aggregate state",
1164 ));
1165 }
1166 let inner = &mut *ctx;
1168 if inner.is_null() {
1172 let obj = Box::into_raw(Box::new(A::default()));
1175 *inner = obj;
1176 }
1177 &mut **inner
1181 };
1182
1183 let connection = unsafe { NonNull::new(ffi::sqlite3_context_db_handle(ctx)) };
1185 let connection = connection.ok_or(SqliteCallbackError::Abort(
1186 "sqlite3_context_db_handle returned a null pointer. This should never happen",
1187 ))?;
1188 let args = build_sql_function_args::<ArgsSqlType, Args>(args, connection)?;
1189
1190 aggregator.step(args);
1191 Ok(())
1192}
1193
1194extern "C" fn run_aggregator_final_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
1195 ctx: *mut ffi::sqlite3_context,
1196) where
1197 A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send,
1198 Args: FromSqlRow<ArgsSqlType, Sqlite>,
1199 Ret: ToSql<RetSqlType, Sqlite>,
1200 Sqlite: HasSqlType<RetSqlType>,
1201{
1202 let result = crate::util::std_compat::catch_unwind(|| {
1203 let aggregator = unsafe {
1204 let ctx = ffi::sqlite3_aggregate_context(
1207 ctx,
1208 0,
1210 )
1211 .cast::<*mut A>();
1213 if ctx.is_null() {
1217 None
1218 } else {
1219 let inner = &mut *ctx;
1223 if inner.is_null() {
1224 None
1226 } else {
1227 let value = Box::from_raw(*inner);
1231 let value = Some(*value);
1232 *inner = core::ptr::null_mut();
1235 value
1236 }
1237 }
1238 };
1239
1240 let res = A::finalize(aggregator);
1241 let value = process_sql_function_result(&res)?;
1242 let r = unsafe { value.result_of(&mut *ctx) };
1244 r.map_err(|e| {
1245 SqliteCallbackError::DieselError(crate::result::Error::SerializationError(Box::new(e)))
1246 })?;
1247 Ok(())
1248 })
1249 .unwrap_or_else(|_e| {
1250 Err(SqliteCallbackError::Panic(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::finalize() panicked",
core::any::type_name::<A>()))
})alloc::format!(
1251 "{}::finalize() panicked",
1252 core::any::type_name::<A>()
1253 )))
1254 });
1255 if let Err(e) = result {
1256 e.emit(ctx);
1257 }
1258}
1259
1260unsafe fn context_error_str(ctx: *mut ffi::sqlite3_context, error: &str) {
1261 let len: i32 = error.len().try_into().unwrap_or(i32::MAX);
1262 unsafe {
1263 ffi::sqlite3_result_error(ctx, error.as_ptr() as *const _, len);
1264 }
1265}
1266
1267struct CollationUserPtr<F> {
1268 callback: F,
1269 collation_name: String,
1270}
1271
1272#[allow(warnings)]
1273extern "C" fn run_collation_function<F>(
1274 user_ptr: *mut libc::c_void,
1275 lhs_len: libc::c_int,
1276 lhs_ptr: *const libc::c_void,
1277 rhs_len: libc::c_int,
1278 rhs_ptr: *const libc::c_void,
1279) -> libc::c_int
1280where
1281 F: Fn(&str, &str) -> core::cmp::Ordering + Send + core::panic::UnwindSafe + 'static,
1282{
1283 let user_ptr = user_ptr as *const CollationUserPtr<F>;
1284 let user_ptr = core::panic::AssertUnwindSafe(unsafe { user_ptr.as_ref() });
1285
1286 let result = crate::util::std_compat::catch_unwind(|| {
1287 let user_ptr = user_ptr.ok_or_else(|| {
1288 SqliteCallbackError::Abort(
1289 "Got a null pointer as data pointer. This should never happen",
1290 )
1291 })?;
1292 for (ptr, len, side) in &[(rhs_ptr, rhs_len, "rhs"), (lhs_ptr, lhs_len, "lhs")] {
1293 if *len < 0 {
1294 {
::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", 1294u32));
};
crate::util::std_compat::abort();assert_fail!(
1295 "An unknown error occurred. {}_len is negative. This should never happen.",
1296 side
1297 );
1298 }
1299 if ptr.is_null() {
1300 {
::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", 1300u32));
};
crate::util::std_compat::abort();assert_fail!(
1301 "An unknown error occurred. {}_ptr is a null pointer. This should never happen.",
1302 side
1303 );
1304 }
1305 }
1306
1307 let (rhs, lhs) = unsafe {
1308 (
1312 str::from_utf8(slice::from_raw_parts(rhs_ptr as *const u8, rhs_len as _)),
1313 str::from_utf8(slice::from_raw_parts(lhs_ptr as *const u8, lhs_len as _)),
1314 )
1315 };
1316
1317 let rhs =
1318 rhs.map_err(|_| SqliteCallbackError::Abort("Got an invalid UTF-8 string for rhs"))?;
1319 let lhs =
1320 lhs.map_err(|_| SqliteCallbackError::Abort("Got an invalid UTF-8 string for lhs"))?;
1321
1322 Ok((user_ptr.callback)(rhs, lhs))
1323 })
1324 .unwrap_or_else(|p| {
1325 Err(SqliteCallbackError::Panic(
1326 user_ptr
1327 .map(|u| u.collation_name.clone())
1328 .unwrap_or_default(),
1329 ))
1330 });
1331
1332 match result {
1333 Ok(core::cmp::Ordering::Less) => -1,
1334 Ok(core::cmp::Ordering::Equal) => 0,
1335 Ok(core::cmp::Ordering::Greater) => 1,
1336 Err(SqliteCallbackError::Abort(a)) => {
1337 #[cfg(feature = "std")]
1338 {
::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!(
1339 "Collation function {} failed with: {}",
1340 user_ptr
1341 .map(|c| &c.collation_name as &str)
1342 .unwrap_or_default(),
1343 a
1344 );
1345 crate::util::std_compat::abort()
1346 }
1347 Err(SqliteCallbackError::DieselError(e)) => {
1348 #[cfg(feature = "std")]
1349 {
::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!(
1350 "Collation function {} failed with: {}",
1351 user_ptr
1352 .map(|c| &c.collation_name as &str)
1353 .unwrap_or_default(),
1354 e
1355 );
1356 crate::util::std_compat::abort()
1357 }
1358 Err(SqliteCallbackError::Panic(msg)) => {
1359 #[cfg(feature = "std")]
1360 {
::std::io::_eprint(format_args!("Collation function {0} panicked\n",
msg));
};eprintln!("Collation function {} panicked", msg);
1361 crate::util::std_compat::abort()
1362 }
1363 }
1364}
1365
1366extern "C" fn destroy_boxed<F>(data: *mut libc::c_void) {
1367 let ptr = data as *mut F;
1368 unsafe { core::mem::drop(Box::from_raw(ptr)) };
1369}
1370
1371unsafe extern "C" fn update_hook_trampoline<F>(
1382 user_data: *mut libc::c_void,
1383 op: libc::c_int,
1384 db_name: *const libc::c_char,
1385 table_name: *const libc::c_char,
1386 rowid: ffi::sqlite3_int64,
1387) where
1388 F: FnMut(SqliteChangeEvent<'_>),
1389{
1390 let result = crate::util::std_compat::catch_unwind(core::panic::AssertUnwindSafe(|| {
1391 let hook = unsafe { &mut *(user_data as *mut F) };
1393
1394 let db_name = unsafe { CStr::from_ptr(db_name) }.to_string_lossy();
1397 let table_name = unsafe { CStr::from_ptr(table_name) }.to_string_lossy();
1398
1399 hook(SqliteChangeEvent {
1400 op: SqliteChangeOp::from_ffi(op),
1401 db_name: &db_name,
1402 table_name: &table_name,
1403 rowid,
1404 });
1405 }));
1406
1407 if result.is_err() {
1408 {
::std::io::_eprint(format_args!("Panic in sqlite3_update_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", 1408u32));
};
crate::util::std_compat::abort();assert_fail!("Panic in sqlite3_update_hook trampoline. ");
1409 }
1410}
1411
1412unsafe extern "C" fn commit_hook_trampoline<F>(user_data: *mut libc::c_void) -> libc::c_int
1418where
1419 F: FnMut() -> CommitDecision,
1420{
1421 let result = crate::util::std_compat::catch_unwind(core::panic::AssertUnwindSafe(|| {
1422 let f = unsafe { &mut *(user_data as *mut F) };
1424 f()
1425 }));
1426
1427 match result {
1428 Ok(CommitDecision::Rollback) => 1,
1429 Ok(CommitDecision::Proceed) => 0,
1430 Err(_) => {
1431 {
::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", 1431u32));
};
crate::util::std_compat::abort();assert_fail!("Panic in sqlite3_commit_hook trampoline. ");
1432 }
1433 }
1434}
1435
1436unsafe extern "C" fn rollback_hook_trampoline<F>(user_data: *mut libc::c_void)
1442where
1443 F: FnMut(),
1444{
1445 let result = crate::util::std_compat::catch_unwind(core::panic::AssertUnwindSafe(|| {
1446 let f = unsafe { &mut *(user_data as *mut F) };
1448 f();
1449 }));
1450
1451 if result.is_err() {
1452 {
::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", 1452u32));
};
crate::util::std_compat::abort();assert_fail!("Panic in sqlite3_rollback_hook trampoline. ");
1453 }
1454}
1455
1456unsafe extern "C" fn progress_handler_trampoline<F>(user_data: *mut libc::c_void) -> libc::c_int
1462where
1463 F: FnMut() -> ProgressDecision,
1464{
1465 let result = crate::util::std_compat::catch_unwind(core::panic::AssertUnwindSafe(|| {
1466 let f = unsafe { &mut *(user_data as *mut F) };
1468 f()
1469 }));
1470
1471 match result {
1472 Ok(ProgressDecision::Interrupt) => 1,
1473 Ok(ProgressDecision::Continue) => 0,
1474 Err(_) => {
1475 {
::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", 1475u32));
};
crate::util::std_compat::abort();assert_fail!("Panic in sqlite3_progress_handler trampoline. ");
1476 }
1477 }
1478}
1479
1480unsafe extern "C" fn wal_hook_trampoline<F>(
1488 user_data: *mut libc::c_void,
1489 db: *mut ffi::sqlite3,
1490 db_name: *const libc::c_char,
1491 n_pages: libc::c_int,
1492) -> libc::c_int
1493where
1494 F: Fn(&'_ mut BorrowedSqliteConnection, &str, u32),
1495{
1496 let result = crate::util::std_compat::catch_unwind(core::panic::AssertUnwindSafe(|| {
1497 let f = unsafe { &*(user_data as *const F) };
1499
1500 let db_name: String = if db_name.is_null() {
1502 String::new()
1503 } else {
1504 unsafe { CStr::from_ptr(db_name) }
1505 .to_string_lossy()
1506 .into_owned()
1507 };
1508 let n_pages = u32::try_from(n_pages).unwrap_or(0);
1510
1511 let Some(db) = NonNull::new(db) else {
1512 return;
1513 };
1514
1515 unsafe {
1520 SqliteConnection::with_borrowed_connection(db, |conn| f(conn, &db_name, n_pages));
1521 }
1522 }));
1523
1524 if result.is_err() {
1525 {
::std::io::_eprint(format_args!("Panic in sqlite3_wal_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", 1525u32));
};
crate::util::std_compat::abort();assert_fail!("Panic in sqlite3_wal_hook trampoline. ");
1526 }
1527
1528 ffi::SQLITE_OK
1529}
1530
1531unsafe extern "C" fn busy_handler_trampoline<F>(
1537 user_data: *mut libc::c_void,
1538 retry_count: libc::c_int,
1539) -> libc::c_int
1540where
1541 F: FnMut(i32) -> BusyDecision,
1542{
1543 let result = crate::util::std_compat::catch_unwind(core::panic::AssertUnwindSafe(|| {
1544 let f = unsafe { &mut *(user_data as *mut F) };
1546 f(retry_count)
1547 }));
1548
1549 match result {
1550 Ok(BusyDecision::Retry) => 1,
1551 Ok(BusyDecision::GiveUp) => 0,
1552 Err(_) => {
1553 {
::std::io::_eprint(format_args!("Panic in sqlite3_busy_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", 1553u32));
};
crate::util::std_compat::abort();assert_fail!("Panic in sqlite3_busy_handler trampoline. ");
1554 }
1555 }
1556}
1557
1558unsafe extern "C" fn authorizer_trampoline<F>(
1564 user_data: *mut libc::c_void,
1565 action_code: libc::c_int,
1566 arg1: *const libc::c_char,
1567 arg2: *const libc::c_char,
1568 db_name: *const libc::c_char,
1569 accessor: *const libc::c_char,
1570) -> libc::c_int
1571where
1572 F: FnMut(AuthorizerContext<'_>) -> AuthorizerDecision,
1573{
1574 fn to_str<'a>(ptr: *const libc::c_char) -> Result<Option<&'a str>, core::str::Utf8Error> {
1576 if ptr.is_null() {
1577 Ok(None)
1578 } else {
1579 unsafe { CStr::from_ptr(ptr) }.to_str().map(Some)
1582 }
1583 }
1584
1585 let result = crate::util::std_compat::catch_unwind(core::panic::AssertUnwindSafe(|| {
1586 let (Ok(arg1), Ok(arg2), Ok(db_name), Ok(accessor)) = (
1588 to_str(arg1),
1589 to_str(arg2),
1590 to_str(db_name),
1591 to_str(accessor),
1592 ) else {
1593 return AuthorizerDecision::Deny;
1594 };
1595
1596 let f = unsafe { &mut *(user_data as *mut F) };
1598
1599 f(AuthorizerContext::from_ffi(
1600 action_code,
1601 arg1,
1602 arg2,
1603 db_name,
1604 accessor,
1605 ))
1606 }));
1607
1608 match result {
1609 Ok(decision) => decision.to_ffi(),
1610 Err(_) => {
1611 {
::std::io::_eprint(format_args!("Panic in sqlite3_set_authorizer 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", 1611u32));
};
crate::util::std_compat::abort();assert_fail!("Panic in sqlite3_set_authorizer trampoline. ");
1612 }
1613 }
1614}
1615
1616unsafe extern "C" fn trace_trampoline<F>(
1622 event_code: libc::c_uint,
1623 user_data: *mut libc::c_void,
1624 p: *mut libc::c_void,
1625 x: *mut libc::c_void,
1626) -> libc::c_int
1627where
1628 F: FnMut(SqliteTraceEvent<'_>) + Send + 'static,
1629{
1630 let result = crate::util::std_compat::catch_unwind(core::panic::AssertUnwindSafe(|| {
1631 let f = unsafe { &mut *(user_data as *mut F) };
1633
1634 match event_code {
1639 TRACE_STMT => {
1640 let stmt_ptr = p as *mut ffi::sqlite3_stmt;
1642 let sql_ptr = x as *const libc::c_char;
1643 if sql_ptr.is_null() {
1644 return;
1645 }
1646 let sql = unsafe { CStr::from_ptr(sql_ptr) }.to_string_lossy();
1649 let readonly =
1650 !stmt_ptr.is_null() && unsafe { ffi::sqlite3_stmt_readonly(stmt_ptr) != 0 };
1651 f(SqliteTraceEvent::Statement {
1652 sql: &sql,
1653 readonly,
1654 });
1655 }
1656 TRACE_PROFILE => {
1657 let stmt_ptr = p as *mut ffi::sqlite3_stmt;
1659 let duration_ns = unsafe {
1660 (x as *const ffi::sqlite3_int64).as_ref()
1662 }
1663 .copied()
1664 .unwrap_or_default()
1665 .cast_unsigned();
1666
1667 let readonly =
1668 !stmt_ptr.is_null() && unsafe { ffi::sqlite3_stmt_readonly(stmt_ptr) != 0 };
1669 let sql_ptr = if stmt_ptr.is_null() {
1670 core::ptr::null()
1671 } else {
1672 unsafe { ffi::sqlite3_sql(stmt_ptr) }
1673 };
1674 let sql = if sql_ptr.is_null() {
1676 Cow::Borrowed("")
1677 } else {
1678 unsafe { CStr::from_ptr(sql_ptr) }.to_string_lossy()
1679 };
1680 f(SqliteTraceEvent::Profile {
1681 sql: &sql,
1682 duration_ns,
1683 readonly,
1684 });
1685 }
1686 TRACE_ROW => f(SqliteTraceEvent::Row),
1687 _ => {}
1691 }
1692 }));
1693
1694 if result.is_err() {
1695 {
::std::io::_eprint(format_args!("Panic in sqlite3_trace_v2 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", 1695u32));
};
crate::util::std_compat::abort();assert_fail!("Panic in sqlite3_trace_v2 trampoline. ");
1696 }
1697
1698 0 }
1700
1701unsafe extern "C" fn collation_needed_trampoline<F>(
1709 user_data: *mut libc::c_void,
1710 db: *mut ffi::sqlite3,
1711 e_text_rep: libc::c_int,
1712 name: *const libc::c_char,
1713) where
1714 F: Fn(CollationNeededContext<'_>) -> Option<Collation>,
1715{
1716 let result = crate::util::std_compat::catch_unwind(core::panic::AssertUnwindSafe(|| {
1717 let f = unsafe { &*(user_data as *const F) };
1720
1721 let name: Cow<'_, str> = if name.is_null() {
1725 Cow::Borrowed("")
1726 } else {
1727 unsafe { CStr::from_ptr(name) }.to_string_lossy()
1728 };
1729
1730 let Some(db) = NonNull::new(db) else {
1731 return;
1732 };
1733
1734 let ctx = CollationNeededContext {
1735 name: &name,
1736 text_rep: SqliteTextRep::from_ffi(e_text_rep),
1737 };
1738
1739 unsafe {
1742 SqliteConnection::with_borrowed_connection(db, |conn| {
1743 if let Some(collation) = f(ctx) {
1744 let _ = conn.inner_mut().register_collation(ctx.name, collation);
1746 }
1747 });
1748 }
1749 }));
1750
1751 if result.is_err() {
1752 {
::std::io::_eprint(format_args!("Panic in sqlite3_collation_needed 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", 1752u32));
};
crate::util::std_compat::abort();assert_fail!("Panic in sqlite3_collation_needed trampoline. ");
1753 }
1754}
1755
1756#[cfg(all(test, not(miri)))]
1761mod tests {
1762 use super::super::update_hook::SqliteChangeOp;
1763 use super::*;
1764 use std::sync::{Arc, Mutex};
1765
1766 fn test_connection() -> RawConnection {
1767 RawConnection::establish(":memory:").expect("failed to establish :memory: connection")
1768 }
1769
1770 #[test]
1771 fn insert_event_dispatched_directly() {
1772 let mut conn = test_connection();
1773 conn.exec("CREATE TABLE t (id INTEGER PRIMARY KEY)")
1774 .unwrap();
1775
1776 let fired = Arc::new(Mutex::new(Vec::new()));
1777 let f2 = fired.clone();
1778 conn.set_update_hook(move |e| {
1779 f2.lock().unwrap().push((e.op, e.rowid));
1780 });
1781
1782 conn.exec("INSERT INTO t VALUES (1)").unwrap();
1783
1784 let events = fired.lock().unwrap();
1785 assert_eq!(events.len(), 1);
1786 assert_eq!(events[0].0, SqliteChangeOp::Insert);
1787 assert_eq!(events[0].1, 1);
1788 }
1789
1790 #[test]
1791 fn consecutive_inserts_dispatch_immediately() {
1792 let mut conn = test_connection();
1793 conn.exec("CREATE TABLE t (id INTEGER PRIMARY KEY)")
1794 .unwrap();
1795
1796 let fired = Arc::new(Mutex::new(Vec::new()));
1797 let f2 = fired.clone();
1798 conn.set_update_hook(move |e| {
1799 f2.lock().unwrap().push(e.rowid);
1800 });
1801
1802 conn.exec("INSERT INTO t VALUES (2); INSERT INTO t VALUES (3)")
1803 .unwrap();
1804
1805 let events = fired.lock().unwrap();
1806 assert_eq!(events.len(), 2);
1807 assert_eq!(events[0], 2);
1808 assert_eq!(events[1], 3);
1809 }
1810
1811 #[test]
1812 fn update_event() {
1813 let mut conn = test_connection();
1814 conn.exec("CREATE TABLE t (id INTEGER PRIMARY KEY, v TEXT)")
1815 .unwrap();
1816 conn.exec("INSERT INTO t VALUES (1, 'a')").unwrap();
1817
1818 let fired = Arc::new(Mutex::new(Vec::new()));
1819 let f2 = fired.clone();
1820 conn.set_update_hook(move |e| {
1821 f2.lock().unwrap().push((e.op, e.rowid));
1822 });
1823
1824 conn.exec("UPDATE t SET v = 'b' WHERE id = 1").unwrap();
1825
1826 let events = fired.lock().unwrap();
1827 assert_eq!(events.len(), 1);
1828 assert_eq!(events[0].0, SqliteChangeOp::Update);
1829 assert_eq!(events[0].1, 1);
1830 }
1831
1832 #[test]
1833 fn delete_event() {
1834 let mut conn = test_connection();
1835 conn.exec("CREATE TABLE t (id INTEGER PRIMARY KEY)")
1836 .unwrap();
1837 conn.exec("INSERT INTO t VALUES (1)").unwrap();
1838
1839 let fired = Arc::new(Mutex::new(Vec::new()));
1840 let f2 = fired.clone();
1841 conn.set_update_hook(move |e| {
1842 f2.lock().unwrap().push((e.op, e.rowid));
1843 });
1844
1845 conn.exec("DELETE FROM t WHERE id = 1").unwrap();
1846
1847 let events = fired.lock().unwrap();
1848 assert_eq!(events.len(), 1);
1849 assert_eq!(events[0].0, SqliteChangeOp::Delete);
1850 assert_eq!(events[0].1, 1);
1851 }
1852
1853 #[test]
1854 fn remove_stops_events() {
1855 let mut conn = test_connection();
1856 conn.exec("CREATE TABLE t (id INTEGER PRIMARY KEY)")
1857 .unwrap();
1858
1859 let fired = Arc::new(Mutex::new(Vec::new()));
1860 let f2 = fired.clone();
1861 conn.set_update_hook(move |e| {
1862 f2.lock().unwrap().push(e.rowid);
1863 });
1864
1865 conn.exec("INSERT INTO t VALUES (1)").unwrap();
1866 assert_eq!(fired.lock().unwrap().len(), 1);
1867
1868 conn.remove_update_hook();
1869 conn.exec("INSERT INTO t VALUES (2)").unwrap();
1870 assert_eq!(fired.lock().unwrap().len(), 1); }
1872
1873 #[test]
1874 fn replacing_hook_drops_old() {
1875 let mut conn = test_connection();
1876 conn.exec("CREATE TABLE t (id INTEGER PRIMARY KEY)")
1877 .unwrap();
1878
1879 let first = Arc::new(Mutex::new(Vec::new()));
1880 let f1 = first.clone();
1881 conn.set_update_hook(move |e| {
1882 f1.lock().unwrap().push(e.rowid);
1883 });
1884
1885 conn.exec("INSERT INTO t VALUES (1)").unwrap();
1886 assert_eq!(first.lock().unwrap().len(), 1);
1887
1888 let second = Arc::new(Mutex::new(Vec::new()));
1890 let f2 = second.clone();
1891 conn.set_update_hook(move |e| {
1892 f2.lock().unwrap().push(e.rowid);
1893 });
1894
1895 conn.exec("INSERT INTO t VALUES (2)").unwrap();
1896 assert_eq!(first.lock().unwrap().len(), 1); assert_eq!(*second.lock().unwrap(), vec![2]);
1898 }
1899
1900 #[test]
1901 fn drop_does_not_panic() {
1902 let mut conn = test_connection();
1903 conn.exec("CREATE TABLE t (id INTEGER PRIMARY KEY)")
1904 .unwrap();
1905
1906 conn.set_update_hook(|_| {});
1907 conn.exec("INSERT INTO t VALUES (1)").unwrap();
1908 drop(conn);
1909 }
1911}