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::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
44macro_rules! assert_fail {
53 ($fmt:expr_2021 $(,$args:tt)*) => {
54 #[cfg(feature = "std")]
55 eprint!(concat!(
56 $fmt,
57 "If you see this message, please open an issue at https://github.com/diesel-rs/diesel/issues/new.\n",
58 "Source location: {}:{}\n",
59 ), $($args,)* file!(), line!());
60 crate::util::std_compat::abort()
61 };
62}
63
64#[allow(missing_debug_implementations, missing_copy_implementations)]
65pub(super) struct RawConnection {
66 pub(super) internal_connection: NonNull<ffi::sqlite3>,
67 update_hook: Option<Box<dyn FnMut(SqliteChangeEvent<'_>) + Send>>,
69 commit_hook: Option<Box<dyn FnMut() -> CommitDecision + Send>>,
71 rollback_hook: Option<Box<dyn FnMut() + Send>>,
73 progress_hook: Option<Box<dyn FnMut() -> ProgressDecision + Send>>,
75 wal_hook: Option<Box<dyn Fn(&mut SqliteConnection, &str, u32) + Send>>,
77 busy_handler: Option<Box<dyn FnMut(i32) -> BusyDecision + Send>>,
79 authorizer_hook: Option<Box<dyn FnMut(AuthorizerContext<'_>) -> AuthorizerDecision + Send>>,
81 trace_hook: Option<Box<dyn FnMut(SqliteTraceEvent<'_>) + Send>>,
83 collation_needed_hook:
85 Option<Box<dyn Fn(&mut SqliteConnection, CollationNeededContext<'_>) + Send>>,
86}
87
88impl RawConnection {
89 pub(super) fn from_ptr(conn: NonNull<ffi::sqlite3>) -> Self {
92 RawConnection {
93 internal_connection: conn,
94 update_hook: None,
95 commit_hook: None,
96 rollback_hook: None,
97 progress_hook: None,
98 wal_hook: None,
99 busy_handler: None,
100 authorizer_hook: None,
101 trace_hook: None,
102 collation_needed_hook: None,
103 }
104 }
105
106 pub(super) fn establish(database_url: &str) -> ConnectionResult<Self> {
107 let mut conn_pointer = ptr::null_mut();
108
109 let database_url = if database_url.starts_with("sqlite://") {
110 CString::new(database_url.replacen("sqlite://", "file:", 1))?
111 } else {
112 CString::new(database_url)?
113 };
114 let flags = ffi::SQLITE_OPEN_READWRITE | ffi::SQLITE_OPEN_CREATE | ffi::SQLITE_OPEN_URI;
115 let connection_status = unsafe {
116 ffi::sqlite3_open_v2(database_url.as_ptr(), &mut conn_pointer, flags, ptr::null())
117 };
118
119 match connection_status {
120 ffi::SQLITE_OK => {
121 let conn_pointer = unsafe { NonNull::new_unchecked(conn_pointer) };
122 Ok(RawConnection {
123 internal_connection: conn_pointer,
124 update_hook: None,
125 commit_hook: None,
126 rollback_hook: None,
127 progress_hook: None,
128 wal_hook: None,
129 busy_handler: None,
130 authorizer_hook: None,
131 trace_hook: None,
132 collation_needed_hook: None,
133 })
134 }
135 err_code => {
136 let message = super::error_message(err_code);
137 unsafe { ffi::sqlite3_close(conn_pointer) };
143 Err(ConnectionError::BadConnection(message.into()))
144 }
145 }
146 }
147
148 pub(super) fn exec(&self, query: &str) -> QueryResult<()> {
149 let query = CString::new(query)?;
150 let callback_fn = None;
151 let callback_arg = ptr::null_mut();
152 let result = unsafe {
153 ffi::sqlite3_exec(
154 self.internal_connection.as_ptr(),
155 query.as_ptr(),
156 callback_fn,
157 callback_arg,
158 ptr::null_mut(),
159 )
160 };
161
162 ensure_sqlite_ok(result, self.internal_connection.as_ptr())
163 }
164
165 pub(super) fn rows_affected_by_last_query(
166 &self,
167 ) -> Result<usize, Box<dyn core::error::Error + Send + Sync>> {
168 let r = unsafe { ffi::sqlite3_changes(self.internal_connection.as_ptr()) };
169
170 Ok(r.try_into()?)
171 }
172
173 pub(super) fn last_insert_rowid(&self) -> i64 {
174 unsafe { ffi::sqlite3_last_insert_rowid(self.internal_connection.as_ptr()) }
175 }
176
177 pub(super) fn register_sql_function<F, Ret, RetSqlType>(
178 &self,
179 fn_name: &str,
180 num_args: usize,
181 behavior: SqliteFunctionBehavior,
182 f: F,
183 ) -> QueryResult<()>
184 where
185 F: FnMut(&Self, &mut [*mut ffi::sqlite3_value]) -> QueryResult<Ret>
186 + core::panic::UnwindSafe
187 + Send
188 + 'static,
189 Ret: ToSql<RetSqlType, Sqlite>,
190 Sqlite: HasSqlType<RetSqlType>,
191 {
192 let c_fn_name = Self::get_fn_name(fn_name)?;
193 let flags = behavior.to_flags();
194 let num_args = num_args
195 .try_into()
196 .map_err(|e| Error::SerializationError(Box::new(e)))?;
197 let callback_fn = Box::into_raw(Box::new(CustomFunctionUserPtr {
200 callback: f,
201 function_name: fn_name.to_owned(),
202 }));
203
204 let result = unsafe {
205 ffi::sqlite3_create_function_v2(
206 self.internal_connection.as_ptr(),
207 c_fn_name.as_ptr(),
208 num_args,
209 flags,
210 callback_fn as *mut _,
211 Some(run_custom_function::<F, Ret, RetSqlType>),
212 None,
213 None,
214 Some(destroy_boxed::<CustomFunctionUserPtr<F>>),
215 )
216 };
217
218 Self::process_sql_function_result(result)
219 }
220
221 pub(super) fn register_aggregate_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
222 &self,
223 fn_name: &str,
224 num_args: usize,
225 behavior: SqliteFunctionBehavior,
226 ) -> QueryResult<()>
227 where
228 A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send + core::panic::UnwindSafe,
229 Args: FromSqlRow<ArgsSqlType, Sqlite>,
230 Ret: ToSql<RetSqlType, Sqlite>,
231 Sqlite: HasSqlType<RetSqlType>,
232 {
233 let fn_name = Self::get_fn_name(fn_name)?;
234 let flags = behavior.to_flags();
235 let num_args = num_args
236 .try_into()
237 .map_err(|e| Error::SerializationError(Box::new(e)))?;
238
239 let result = unsafe {
240 ffi::sqlite3_create_function_v2(
241 self.internal_connection.as_ptr(),
242 fn_name.as_ptr(),
243 num_args,
244 flags,
245 core::ptr::null_mut(),
246 None,
247 Some(run_aggregator_step_function::<_, _, _, _, A>),
248 Some(run_aggregator_final_function::<_, _, _, _, A>),
249 None,
250 )
251 };
252
253 Self::process_sql_function_result(result)
254 }
255
256 pub(super) fn register_collation_function<F>(
257 &self,
258 collation_name: &str,
259 collation: F,
260 ) -> QueryResult<()>
261 where
262 F: Fn(&str, &str) -> core::cmp::Ordering + core::panic::UnwindSafe + Send + 'static,
263 {
264 let c_collation_name = Self::get_fn_name(collation_name)?;
265 let callback_fn = Box::into_raw(Box::new(CollationUserPtr {
267 callback: collation,
268 collation_name: collation_name.to_owned(),
269 }));
270
271 let result = unsafe {
272 ffi::sqlite3_create_collation_v2(
273 self.internal_connection.as_ptr(),
274 c_collation_name.as_ptr(),
275 ffi::SQLITE_UTF8,
276 callback_fn as *mut _,
277 Some(run_collation_function::<F>),
278 Some(destroy_boxed::<CollationUserPtr<F>>),
279 )
280 };
281
282 let result = Self::process_sql_function_result(result);
283 if result.is_err() {
284 destroy_boxed::<CollationUserPtr<F>>(callback_fn as *mut _);
285 }
286 result
287 }
288
289 pub(super) fn serialize(&mut self) -> SerializedDatabase {
290 unsafe {
291 let mut size: ffi::sqlite3_int64 = 0;
292 let data_ptr = ffi::sqlite3_serialize(
293 self.internal_connection.as_ptr(),
294 core::ptr::null(),
295 &mut size as *mut _,
296 0,
297 );
298 SerializedDatabase::new(
299 data_ptr,
300 size.try_into()
301 .expect("Cannot fit the serialized database into memory"),
302 )
303 }
304 }
305
306 pub(super) unsafe fn deserialize(&mut self, data: &[u8]) -> QueryResult<()> {
311 let db_size = data
312 .len()
313 .try_into()
314 .map_err(|e| Error::DeserializationError(Box::new(e)))?;
315 #[allow(clippy::unnecessary_cast)]
317 unsafe {
318 let result = ffi::sqlite3_deserialize(
319 self.internal_connection.as_ptr(),
320 core::ptr::null(),
321 data.as_ptr() as *mut u8,
322 db_size,
323 db_size,
324 ffi::SQLITE_DESERIALIZE_READONLY as u32,
325 );
326
327 ensure_sqlite_ok(result, self.internal_connection.as_ptr())
328 }
329 }
330
331 pub(super) fn set_limit(&self, limit: SqliteLimit, value: i32) -> i32 {
332 unsafe { ffi::sqlite3_limit(self.internal_connection.as_ptr(), limit.to_ffi(), value) }
333 }
334
335 pub(super) fn get_limit(&self, limit: SqliteLimit) -> i32 {
336 unsafe {
337 ffi::sqlite3_limit(self.internal_connection.as_ptr(), limit.to_ffi(), -1)
339 }
340 }
341
342 pub(super) fn set_db_config_bool(&self, op: i32, value: bool) -> QueryResult<()> {
344 let mut result_value: libc::c_int = 0;
345 let new_value: libc::c_int = if value { 1 } else { 0 };
346
347 let result = unsafe {
348 ffi::sqlite3_db_config(
349 self.internal_connection.as_ptr(),
350 op,
351 new_value,
352 &mut result_value as *mut libc::c_int,
353 )
354 };
355
356 ensure_sqlite_ok(result, self.internal_connection.as_ptr())
357 }
358
359 pub(super) fn get_db_config_bool(&self, op: i32) -> QueryResult<bool> {
361 let mut current_value: libc::c_int = 0;
362
363 let result = unsafe {
364 ffi::sqlite3_db_config(
365 self.internal_connection.as_ptr(),
366 op,
367 -1_i32, &mut current_value as *mut libc::c_int,
369 )
370 };
371
372 ensure_sqlite_ok(result, self.internal_connection.as_ptr())?;
373 Ok(current_value != 0)
374 }
375
376 fn get_fn_name(fn_name: &str) -> Result<CString, NulError> {
377 CString::new(fn_name)
378 }
379
380 fn process_sql_function_result(result: i32) -> Result<(), Error> {
381 if result == ffi::SQLITE_OK {
382 Ok(())
383 } else {
384 let error_message = super::error_message(result);
385 Err(DatabaseError(
386 DatabaseErrorKind::Unknown,
387 Box::new(error_message.to_string()),
388 ))
389 }
390 }
391
392 pub(super) fn blob_open<'conn>(
393 &'conn self,
394 database_name: &str,
395 table_name: &str,
396 column_name: &str,
397 row_id: i64,
398 ) -> Result<super::sqlite_blob::SqliteReadOnlyBlob<'conn>, Error> {
399 let database_name = alloc::ffi::CString::new(database_name)?;
400 let column_name = alloc::ffi::CString::new(column_name)?;
401 let table_name = alloc::ffi::CString::new(table_name)?;
402
403 let mut blob: *mut ffi::sqlite3_blob = core::ptr::null_mut();
404
405 let ret = unsafe {
407 ffi::sqlite3_blob_open(
408 self.internal_connection.as_ptr(),
409 database_name.as_c_str().as_ptr(),
410 table_name.as_c_str().as_ptr(),
411 column_name.as_c_str().as_ptr(),
412 row_id,
413 0,
414 &mut blob,
415 )
416 };
417
418 Self::process_sql_function_result(ret)?;
419
420 let blob = unsafe { core::ptr::NonNull::new_unchecked(blob) };
428
429 let blob_size = unsafe { ffi::sqlite3_blob_bytes(blob.as_ptr()) };
431 let blob_size = usize::try_from(blob_size).map_err(Error::IntegerConversion)?;
432
433 Ok(super::sqlite_blob::SqliteReadOnlyBlob {
434 blob,
435 read_index: 0,
436 blob_size,
437 _pd: core::marker::PhantomData,
438 })
439 }
440
441 pub(super) fn set_update_hook<F>(&mut self, hook: F)
453 where
454 F: FnMut(SqliteChangeEvent<'_>) + Send + 'static,
455 {
456 let mut boxed: Box<dyn FnMut(SqliteChangeEvent<'_>) + Send> = Box::new(hook);
457 let ptr = &raw mut *boxed as *mut libc::c_void;
458
459 unsafe {
460 ffi::sqlite3_update_hook(
461 self.internal_connection.as_ptr(),
462 Some(update_hook_trampoline::<F>),
463 ptr,
464 );
465 }
466
467 self.update_hook = Some(boxed);
470 }
471
472 pub(super) fn remove_update_hook(&mut self) {
481 unsafe {
482 ffi::sqlite3_update_hook(self.internal_connection.as_ptr(), None, ptr::null_mut());
483 }
484 self.update_hook = None;
485 }
486
487 pub(super) fn set_commit_hook<F>(&mut self, hook: F)
497 where
498 F: FnMut() -> CommitDecision + Send + 'static,
499 {
500 let mut boxed: Box<dyn FnMut() -> CommitDecision + Send> = Box::new(hook);
501 let ptr = &raw mut *boxed as *mut libc::c_void;
502
503 unsafe {
504 ffi::sqlite3_commit_hook(
505 self.internal_connection.as_ptr(),
506 Some(commit_hook_trampoline::<F>),
507 ptr,
508 );
509 }
510
511 self.commit_hook = Some(boxed);
514 }
515
516 pub(super) fn remove_commit_hook(&mut self) {
526 unsafe {
527 ffi::sqlite3_commit_hook(self.internal_connection.as_ptr(), None, ptr::null_mut());
528 }
529 self.commit_hook = None;
530 }
531
532 pub(super) fn set_rollback_hook<F>(&mut self, hook: F)
542 where
543 F: FnMut() + Send + 'static,
544 {
545 let mut boxed: Box<dyn FnMut() + Send> = Box::new(hook);
546 let ptr = &raw mut *boxed as *mut libc::c_void;
547
548 unsafe {
549 ffi::sqlite3_rollback_hook(
550 self.internal_connection.as_ptr(),
551 Some(rollback_hook_trampoline::<F>),
552 ptr,
553 );
554 }
555
556 self.rollback_hook = Some(boxed);
559 }
560
561 pub(super) fn remove_rollback_hook(&mut self) {
571 unsafe {
572 ffi::sqlite3_rollback_hook(self.internal_connection.as_ptr(), None, ptr::null_mut());
573 }
574 self.rollback_hook = None;
575 }
576
577 pub(super) fn set_progress_handler<F>(&mut self, n: NonZeroU32, hook: F)
589 where
590 F: FnMut() -> ProgressDecision + Send + 'static,
591 {
592 let mut boxed: Box<dyn FnMut() -> ProgressDecision + Send> = Box::new(hook);
593 let ptr = &raw mut *boxed as *mut libc::c_void;
594
595 let n = i32::try_from(n.get()).unwrap_or(i32::MAX);
599
600 unsafe {
601 ffi::sqlite3_progress_handler(
602 self.internal_connection.as_ptr(),
603 n,
604 Some(progress_handler_trampoline::<F>),
605 ptr,
606 );
607 }
608
609 self.progress_hook = Some(boxed);
612 }
613
614 pub(super) fn remove_progress_handler(&mut self) {
624 unsafe {
625 ffi::sqlite3_progress_handler(
626 self.internal_connection.as_ptr(),
627 0,
628 None,
629 ptr::null_mut(),
630 );
631 }
632 self.progress_hook = None;
633 }
634
635 pub(super) fn set_wal_hook<F>(&mut self, hook: F)
648 where
649 F: Fn(&mut SqliteConnection, &str, u32) + Send + 'static,
650 {
651 let boxed: Box<dyn Fn(&mut SqliteConnection, &str, u32) + Send> = Box::new(hook);
652 let ptr = &raw const *boxed as *mut libc::c_void;
653
654 unsafe {
655 ffi::sqlite3_wal_hook(
656 self.internal_connection.as_ptr(),
657 Some(wal_hook_trampoline::<F>),
658 ptr,
659 );
660 }
661
662 self.wal_hook = Some(boxed);
665 }
666
667 pub(super) fn remove_wal_hook(&mut self) {
677 unsafe {
678 ffi::sqlite3_wal_hook(self.internal_connection.as_ptr(), None, ptr::null_mut());
679 }
680 self.wal_hook = None;
681 }
682
683 pub(super) fn set_busy_handler<F>(&mut self, hook: F)
696 where
697 F: FnMut(i32) -> BusyDecision + Send + 'static,
698 {
699 let mut boxed: Box<dyn FnMut(i32) -> BusyDecision + Send> = Box::new(hook);
700 let ptr = &raw mut *boxed as *mut libc::c_void;
701
702 unsafe {
703 ffi::sqlite3_busy_handler(
704 self.internal_connection.as_ptr(),
705 Some(busy_handler_trampoline::<F>),
706 ptr,
707 );
708 }
709
710 self.busy_handler = Some(boxed);
713 }
714
715 pub(super) fn remove_busy_handler(&mut self) {
725 unsafe {
726 ffi::sqlite3_busy_handler(self.internal_connection.as_ptr(), None, ptr::null_mut());
727 }
728 self.busy_handler = None;
729 }
730
731 pub(super) fn set_busy_timeout(&mut self, ms: i32) {
743 unsafe {
744 ffi::sqlite3_busy_timeout(self.internal_connection.as_ptr(), ms);
745 }
746 self.busy_handler = None;
747 }
748
749 pub(super) fn set_authorizer<F>(&mut self, hook: F)
760 where
761 F: FnMut(AuthorizerContext<'_>) -> AuthorizerDecision + Send + 'static,
762 {
763 let mut boxed: Box<dyn FnMut(AuthorizerContext<'_>) -> AuthorizerDecision + Send> =
764 Box::new(hook);
765 let ptr = &raw mut *boxed as *mut libc::c_void;
766
767 unsafe {
768 ffi::sqlite3_set_authorizer(
769 self.internal_connection.as_ptr(),
770 Some(authorizer_trampoline::<F>),
771 ptr,
772 );
773 }
774
775 self.authorizer_hook = Some(boxed);
778 }
779
780 pub(super) fn remove_authorizer(&mut self) {
790 unsafe {
791 ffi::sqlite3_set_authorizer(self.internal_connection.as_ptr(), None, ptr::null_mut());
792 }
793 self.authorizer_hook = None;
794 }
795
796 pub(super) fn set_trace<F>(&mut self, mask: SqliteTraceFlags, hook: F)
809 where
810 F: FnMut(SqliteTraceEvent<'_>) + Send + 'static,
811 {
812 let mut boxed: Box<dyn FnMut(SqliteTraceEvent<'_>) + Send> = Box::new(hook);
813 let ptr = &raw mut *boxed as *mut libc::c_void;
814
815 unsafe {
816 ffi::sqlite3_trace_v2(
817 self.internal_connection.as_ptr(),
818 mask.bits(),
819 Some(trace_trampoline::<F>),
820 ptr,
821 );
822 }
823
824 self.trace_hook = Some(boxed);
827 }
828
829 pub(super) fn remove_trace(&mut self) {
839 unsafe {
840 ffi::sqlite3_trace_v2(self.internal_connection.as_ptr(), 0, None, ptr::null_mut());
841 }
842 self.trace_hook = None;
843 }
844
845 pub(super) fn set_collation_needed_hook<F>(&mut self, hook: F)
853 where
854 F: Fn(&mut SqliteConnection, CollationNeededContext<'_>) + Send + 'static,
855 {
856 let boxed: Box<dyn Fn(&mut SqliteConnection, CollationNeededContext<'_>) + Send> =
857 Box::new(hook);
858 let ptr = &raw const *boxed as *mut libc::c_void;
859
860 unsafe {
861 ffi::sqlite3_collation_needed(
862 self.internal_connection.as_ptr(),
863 ptr,
864 Some(collation_needed_trampoline::<F>),
865 );
866 }
867
868 self.collation_needed_hook = Some(boxed);
871 }
872
873 pub(super) fn remove_collation_needed_hook(&mut self) {
881 unsafe {
882 ffi::sqlite3_collation_needed(self.internal_connection.as_ptr(), ptr::null_mut(), None);
883 }
884 self.collation_needed_hook = None;
885 }
886}
887
888impl Drop for RawConnection {
889 fn drop(&mut self) {
890 use crate::util::std_compat::panicking;
891
892 self.remove_update_hook();
894 self.remove_commit_hook();
895 self.remove_rollback_hook();
896 self.remove_progress_handler();
897 self.remove_wal_hook();
898 self.remove_busy_handler();
899 self.remove_authorizer();
900 self.remove_trace();
901 self.remove_collation_needed_hook();
902
903 let close_result = unsafe { ffi::sqlite3_close(self.internal_connection.as_ptr()) };
904 if close_result != ffi::SQLITE_OK {
905 let error_message = super::error_message(close_result);
906 if panicking() {
907 #[cfg(feature = "std")]
908 {
::std::io::_eprint(format_args!("Error closing SQLite connection: {0}\n",
error_message));
};eprintln!("Error closing SQLite connection: {error_message}");
909 } else {
910 {
::core::panicking::panic_fmt(format_args!("Error closing SQLite connection: {0}",
error_message));
};panic!("Error closing SQLite connection: {error_message}");
911 }
912 }
913 }
914}
915
916enum SqliteCallbackError {
917 Abort(&'static str),
918 DieselError(crate::result::Error),
919 Panic(String),
920}
921
922impl SqliteCallbackError {
923 fn emit(&self, ctx: *mut ffi::sqlite3_context) {
924 let s;
925 let msg = match self {
926 SqliteCallbackError::Abort(msg) => *msg,
927 SqliteCallbackError::DieselError(e) => {
928 s = e.to_string();
929 &s
930 }
931 SqliteCallbackError::Panic(msg) => msg,
932 };
933 unsafe {
934 context_error_str(ctx, msg);
935 }
936 }
937}
938
939impl From<crate::result::Error> for SqliteCallbackError {
940 fn from(e: crate::result::Error) -> Self {
941 Self::DieselError(e)
942 }
943}
944
945struct CustomFunctionUserPtr<F> {
946 callback: F,
947 function_name: String,
948}
949
950#[allow(warnings)]
951extern "C" fn run_custom_function<F, Ret, RetSqlType>(
952 ctx: *mut ffi::sqlite3_context,
953 num_args: libc::c_int,
954 value_ptr: *mut *mut ffi::sqlite3_value,
955) where
956 F: FnMut(&RawConnection, &mut [*mut ffi::sqlite3_value]) -> QueryResult<Ret>
957 + core::panic::UnwindSafe
958 + Send
959 + 'static,
960 Ret: ToSql<RetSqlType, Sqlite>,
961 Sqlite: HasSqlType<RetSqlType>,
962{
963 use core::ops::Deref;
964 static NULL_DATA_ERR: &str = "An unknown error occurred. sqlite3_user_data returned a null pointer. This should never happen.";
965 static NULL_CONN_ERR: &str = "An unknown error occurred. sqlite3_context_db_handle returned a null pointer. This should never happen.";
966
967 let conn = match unsafe { NonNull::new(ffi::sqlite3_context_db_handle(ctx)) } {
968 Some(conn) => mem::ManuallyDrop::new(RawConnection::from_ptr(conn)),
971 None => {
972 unsafe { context_error_str(ctx, NULL_CONN_ERR) };
973 return;
974 }
975 };
976
977 let data_ptr = unsafe { ffi::sqlite3_user_data(ctx) };
978
979 let mut data_ptr = match NonNull::new(data_ptr as *mut CustomFunctionUserPtr<F>) {
980 None => unsafe {
981 context_error_str(ctx, NULL_DATA_ERR);
982 return;
983 },
984 Some(mut f) => f,
985 };
986 let data_ptr = unsafe { data_ptr.as_mut() };
987
988 let callback = core::panic::AssertUnwindSafe(&mut data_ptr.callback);
991 let conn = core::panic::AssertUnwindSafe(conn);
994
995 let result = crate::util::std_compat::catch_unwind(move || {
996 let _ = &callback;
997 let args = unsafe { slice::from_raw_parts_mut(value_ptr, num_args as _) };
998 let res = (callback.0)(&*conn, args)?;
999 let value = process_sql_function_result(&res)?;
1000 unsafe {
1002 value.result_of(&mut *ctx);
1003 }
1004 Ok(())
1005 })
1006 .unwrap_or_else(|p| Err(SqliteCallbackError::Panic(data_ptr.function_name.clone())));
1007 if let Err(e) = result {
1008 e.emit(ctx);
1009 }
1010}
1011
1012#[allow(warnings)]
1013extern "C" fn run_aggregator_step_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
1014 ctx: *mut ffi::sqlite3_context,
1015 num_args: libc::c_int,
1016 value_ptr: *mut *mut ffi::sqlite3_value,
1017) where
1018 A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send + core::panic::UnwindSafe,
1019 Args: FromSqlRow<ArgsSqlType, Sqlite>,
1020 Ret: ToSql<RetSqlType, Sqlite>,
1021 Sqlite: HasSqlType<RetSqlType>,
1022{
1023 let result = crate::util::std_compat::catch_unwind(move || {
1024 let args = unsafe { slice::from_raw_parts_mut(value_ptr, num_args as _) };
1025 run_aggregator_step::<A, Args, ArgsSqlType>(ctx, args)
1026 })
1027 .unwrap_or_else(|e| {
1028 Err(SqliteCallbackError::Panic(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::step() panicked",
core::any::type_name::<A>()))
})alloc::format!(
1029 "{}::step() panicked",
1030 core::any::type_name::<A>()
1031 )))
1032 });
1033
1034 match result {
1035 Ok(()) => {}
1036 Err(e) => e.emit(ctx),
1037 }
1038}
1039
1040fn run_aggregator_step<A, Args, ArgsSqlType>(
1041 ctx: *mut ffi::sqlite3_context,
1042 args: &mut [*mut ffi::sqlite3_value],
1043) -> Result<(), SqliteCallbackError>
1044where
1045 A: SqliteAggregateFunction<Args>,
1046 Args: FromSqlRow<ArgsSqlType, Sqlite>,
1047{
1048 let aggregator = unsafe {
1049 const {
1050 if core::mem::size_of::<*mut A>() == 0 {
1051 {
::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!(
1052 "The pointer size is zero, that's unexpected.\
1053 If you ever see this error message open a issue\
1054 describing your environment"
1055 );
1056 }
1057 }
1058 let ctx = ffi::sqlite3_aggregate_context(
1065 ctx,
1066 core::mem::size_of::<*mut A>()
1067 .try_into()
1068 .expect("Memory size of a pointer is smaller than i32::MAX"),
1069 )
1070 .cast::<*mut A>();
1072 let inner = &mut *ctx;
1074 if inner.is_null() {
1078 let obj = Box::into_raw(Box::new(A::default()));
1081 *inner = obj;
1082 }
1083 &mut **inner
1087 };
1088
1089 let args = build_sql_function_args::<ArgsSqlType, Args>(args)?;
1090
1091 aggregator.step(args);
1092 Ok(())
1093}
1094
1095extern "C" fn run_aggregator_final_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
1096 ctx: *mut ffi::sqlite3_context,
1097) where
1098 A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send,
1099 Args: FromSqlRow<ArgsSqlType, Sqlite>,
1100 Ret: ToSql<RetSqlType, Sqlite>,
1101 Sqlite: HasSqlType<RetSqlType>,
1102{
1103 let result = crate::util::std_compat::catch_unwind(|| {
1104 let aggregator = unsafe {
1105 let ctx = ffi::sqlite3_aggregate_context(
1108 ctx,
1109 0,
1111 )
1112 .cast::<*mut A>();
1114 if ctx.is_null() {
1118 None
1119 } else {
1120 let inner = &mut *ctx;
1124 if inner.is_null() {
1125 None
1127 } else {
1128 let value = Box::from_raw(*inner);
1132 let value = Some(*value);
1133 *inner = core::ptr::null_mut();
1136 value
1137 }
1138 }
1139 };
1140
1141 let res = A::finalize(aggregator);
1142 let value = process_sql_function_result(&res)?;
1143 let r = unsafe { value.result_of(&mut *ctx) };
1145 r.map_err(|e| {
1146 SqliteCallbackError::DieselError(crate::result::Error::SerializationError(Box::new(e)))
1147 })?;
1148 Ok(())
1149 })
1150 .unwrap_or_else(|_e| {
1151 Err(SqliteCallbackError::Panic(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::finalize() panicked",
core::any::type_name::<A>()))
})alloc::format!(
1152 "{}::finalize() panicked",
1153 core::any::type_name::<A>()
1154 )))
1155 });
1156 if let Err(e) = result {
1157 e.emit(ctx);
1158 }
1159}
1160
1161unsafe fn context_error_str(ctx: *mut ffi::sqlite3_context, error: &str) {
1162 let len: i32 = error.len().try_into().unwrap_or(i32::MAX);
1163 unsafe {
1164 ffi::sqlite3_result_error(ctx, error.as_ptr() as *const _, len);
1165 }
1166}
1167
1168struct CollationUserPtr<F> {
1169 callback: F,
1170 collation_name: String,
1171}
1172
1173#[allow(warnings)]
1174extern "C" fn run_collation_function<F>(
1175 user_ptr: *mut libc::c_void,
1176 lhs_len: libc::c_int,
1177 lhs_ptr: *const libc::c_void,
1178 rhs_len: libc::c_int,
1179 rhs_ptr: *const libc::c_void,
1180) -> libc::c_int
1181where
1182 F: Fn(&str, &str) -> core::cmp::Ordering + Send + core::panic::UnwindSafe + 'static,
1183{
1184 let user_ptr = user_ptr as *const CollationUserPtr<F>;
1185 let user_ptr = core::panic::AssertUnwindSafe(unsafe { user_ptr.as_ref() });
1186
1187 let result = crate::util::std_compat::catch_unwind(|| {
1188 let user_ptr = user_ptr.ok_or_else(|| {
1189 SqliteCallbackError::Abort(
1190 "Got a null pointer as data pointer. This should never happen",
1191 )
1192 })?;
1193 for (ptr, len, side) in &[(rhs_ptr, rhs_len, "rhs"), (lhs_ptr, lhs_len, "lhs")] {
1194 if *len < 0 {
1195 {
::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", 1195u32));
};
crate::util::std_compat::abort();assert_fail!(
1196 "An unknown error occurred. {}_len is negative. This should never happen.",
1197 side
1198 );
1199 }
1200 if ptr.is_null() {
1201 {
::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", 1201u32));
};
crate::util::std_compat::abort();assert_fail!(
1202 "An unknown error occurred. {}_ptr is a null pointer. This should never happen.",
1203 side
1204 );
1205 }
1206 }
1207
1208 let (rhs, lhs) = unsafe {
1209 (
1213 str::from_utf8(slice::from_raw_parts(rhs_ptr as *const u8, rhs_len as _)),
1214 str::from_utf8(slice::from_raw_parts(lhs_ptr as *const u8, lhs_len as _)),
1215 )
1216 };
1217
1218 let rhs =
1219 rhs.map_err(|_| SqliteCallbackError::Abort("Got an invalid UTF-8 string for rhs"))?;
1220 let lhs =
1221 lhs.map_err(|_| SqliteCallbackError::Abort("Got an invalid UTF-8 string for lhs"))?;
1222
1223 Ok((user_ptr.callback)(rhs, lhs))
1224 })
1225 .unwrap_or_else(|p| {
1226 Err(SqliteCallbackError::Panic(
1227 user_ptr
1228 .map(|u| u.collation_name.clone())
1229 .unwrap_or_default(),
1230 ))
1231 });
1232
1233 match result {
1234 Ok(core::cmp::Ordering::Less) => -1,
1235 Ok(core::cmp::Ordering::Equal) => 0,
1236 Ok(core::cmp::Ordering::Greater) => 1,
1237 Err(SqliteCallbackError::Abort(a)) => {
1238 #[cfg(feature = "std")]
1239 {
::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!(
1240 "Collation function {} failed with: {}",
1241 user_ptr
1242 .map(|c| &c.collation_name as &str)
1243 .unwrap_or_default(),
1244 a
1245 );
1246 crate::util::std_compat::abort()
1247 }
1248 Err(SqliteCallbackError::DieselError(e)) => {
1249 #[cfg(feature = "std")]
1250 {
::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!(
1251 "Collation function {} failed with: {}",
1252 user_ptr
1253 .map(|c| &c.collation_name as &str)
1254 .unwrap_or_default(),
1255 e
1256 );
1257 crate::util::std_compat::abort()
1258 }
1259 Err(SqliteCallbackError::Panic(msg)) => {
1260 #[cfg(feature = "std")]
1261 {
::std::io::_eprint(format_args!("Collation function {0} panicked\n",
msg));
};eprintln!("Collation function {} panicked", msg);
1262 crate::util::std_compat::abort()
1263 }
1264 }
1265}
1266
1267extern "C" fn destroy_boxed<F>(data: *mut libc::c_void) {
1268 let ptr = data as *mut F;
1269 unsafe { core::mem::drop(Box::from_raw(ptr)) };
1270}
1271
1272unsafe extern "C" fn update_hook_trampoline<F>(
1283 user_data: *mut libc::c_void,
1284 op: libc::c_int,
1285 db_name: *const libc::c_char,
1286 table_name: *const libc::c_char,
1287 rowid: ffi::sqlite3_int64,
1288) where
1289 F: FnMut(SqliteChangeEvent<'_>),
1290{
1291 let result = crate::util::std_compat::catch_unwind(core::panic::AssertUnwindSafe(|| {
1292 let hook = unsafe { &mut *(user_data as *mut F) };
1294
1295 let db_name = unsafe { CStr::from_ptr(db_name) }.to_string_lossy();
1298 let table_name = unsafe { CStr::from_ptr(table_name) }.to_string_lossy();
1299
1300 hook(SqliteChangeEvent {
1301 op: SqliteChangeOp::from_ffi(op),
1302 db_name: &db_name,
1303 table_name: &table_name,
1304 rowid,
1305 });
1306 }));
1307
1308 if result.is_err() {
1309 {
::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", 1309u32));
};
crate::util::std_compat::abort();assert_fail!("Panic in sqlite3_update_hook trampoline. ");
1310 }
1311}
1312
1313unsafe extern "C" fn commit_hook_trampoline<F>(user_data: *mut libc::c_void) -> libc::c_int
1319where
1320 F: FnMut() -> CommitDecision,
1321{
1322 let result = crate::util::std_compat::catch_unwind(core::panic::AssertUnwindSafe(|| {
1323 let f = unsafe { &mut *(user_data as *mut F) };
1325 f()
1326 }));
1327
1328 match result {
1329 Ok(CommitDecision::Rollback) => 1,
1330 Ok(CommitDecision::Proceed) => 0,
1331 Err(_) => {
1332 {
::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", 1332u32));
};
crate::util::std_compat::abort();assert_fail!("Panic in sqlite3_commit_hook trampoline. ");
1333 }
1334 }
1335}
1336
1337unsafe extern "C" fn rollback_hook_trampoline<F>(user_data: *mut libc::c_void)
1343where
1344 F: FnMut(),
1345{
1346 let result = crate::util::std_compat::catch_unwind(core::panic::AssertUnwindSafe(|| {
1347 let f = unsafe { &mut *(user_data as *mut F) };
1349 f();
1350 }));
1351
1352 if result.is_err() {
1353 {
::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", 1353u32));
};
crate::util::std_compat::abort();assert_fail!("Panic in sqlite3_rollback_hook trampoline. ");
1354 }
1355}
1356
1357unsafe extern "C" fn progress_handler_trampoline<F>(user_data: *mut libc::c_void) -> libc::c_int
1363where
1364 F: FnMut() -> ProgressDecision,
1365{
1366 let result = crate::util::std_compat::catch_unwind(core::panic::AssertUnwindSafe(|| {
1367 let f = unsafe { &mut *(user_data as *mut F) };
1369 f()
1370 }));
1371
1372 match result {
1373 Ok(ProgressDecision::Interrupt) => 1,
1374 Ok(ProgressDecision::Continue) => 0,
1375 Err(_) => {
1376 {
::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", 1376u32));
};
crate::util::std_compat::abort();assert_fail!("Panic in sqlite3_progress_handler trampoline. ");
1377 }
1378 }
1379}
1380
1381unsafe extern "C" fn wal_hook_trampoline<F>(
1389 user_data: *mut libc::c_void,
1390 db: *mut ffi::sqlite3,
1391 db_name: *const libc::c_char,
1392 n_pages: libc::c_int,
1393) -> libc::c_int
1394where
1395 F: Fn(&mut SqliteConnection, &str, u32),
1396{
1397 let result = crate::util::std_compat::catch_unwind(core::panic::AssertUnwindSafe(|| {
1398 let f = unsafe { &*(user_data as *const F) };
1400
1401 let db_name: Cow<'_, str> = if db_name.is_null() {
1405 Cow::Borrowed("")
1406 } else {
1407 unsafe { CStr::from_ptr(db_name) }.to_string_lossy()
1408 };
1409 let n_pages = u32::try_from(n_pages).unwrap_or(0);
1411
1412 let Some(db) = NonNull::new(db) else {
1413 return;
1414 };
1415
1416 unsafe {
1421 SqliteConnection::with_borrowed_connection(db, |conn| f(conn, &db_name, n_pages));
1422 }
1423 }));
1424
1425 if result.is_err() {
1426 {
::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", 1426u32));
};
crate::util::std_compat::abort();assert_fail!("Panic in sqlite3_wal_hook trampoline. ");
1427 }
1428
1429 ffi::SQLITE_OK
1430}
1431
1432unsafe extern "C" fn busy_handler_trampoline<F>(
1438 user_data: *mut libc::c_void,
1439 retry_count: libc::c_int,
1440) -> libc::c_int
1441where
1442 F: FnMut(i32) -> BusyDecision,
1443{
1444 let result = crate::util::std_compat::catch_unwind(core::panic::AssertUnwindSafe(|| {
1445 let f = unsafe { &mut *(user_data as *mut F) };
1447 f(retry_count)
1448 }));
1449
1450 match result {
1451 Ok(BusyDecision::Retry) => 1,
1452 Ok(BusyDecision::GiveUp) => 0,
1453 Err(_) => {
1454 {
::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", 1454u32));
};
crate::util::std_compat::abort();assert_fail!("Panic in sqlite3_busy_handler trampoline. ");
1455 }
1456 }
1457}
1458
1459unsafe extern "C" fn authorizer_trampoline<F>(
1465 user_data: *mut libc::c_void,
1466 action_code: libc::c_int,
1467 arg1: *const libc::c_char,
1468 arg2: *const libc::c_char,
1469 db_name: *const libc::c_char,
1470 accessor: *const libc::c_char,
1471) -> libc::c_int
1472where
1473 F: FnMut(AuthorizerContext<'_>) -> AuthorizerDecision,
1474{
1475 fn to_str<'a>(ptr: *const libc::c_char) -> Option<&'a str> {
1480 if ptr.is_null() {
1481 None
1482 } else {
1483 unsafe { CStr::from_ptr(ptr) }.to_str().ok()
1486 }
1487 }
1488
1489 let result = crate::util::std_compat::catch_unwind(core::panic::AssertUnwindSafe(|| {
1490 let f = unsafe { &mut *(user_data as *mut F) };
1492
1493 let ctx = AuthorizerContext::from_ffi(
1494 action_code,
1495 to_str(arg1),
1496 to_str(arg2),
1497 to_str(db_name),
1498 to_str(accessor),
1499 );
1500
1501 f(ctx)
1502 }));
1503
1504 match result {
1505 Ok(decision) => decision.to_ffi(),
1506 Err(_) => {
1507 {
::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", 1507u32));
};
crate::util::std_compat::abort();assert_fail!("Panic in sqlite3_set_authorizer trampoline. ");
1508 }
1509 }
1510}
1511
1512unsafe extern "C" fn trace_trampoline<F>(
1518 event_code: libc::c_uint,
1519 user_data: *mut libc::c_void,
1520 p: *mut libc::c_void,
1521 x: *mut libc::c_void,
1522) -> libc::c_int
1523where
1524 F: FnMut(SqliteTraceEvent<'_>) + Send + 'static,
1525{
1526 let result = crate::util::std_compat::catch_unwind(core::panic::AssertUnwindSafe(|| {
1527 let f = unsafe { &mut *(user_data as *mut F) };
1529
1530 match event_code {
1535 TRACE_STMT => {
1536 let stmt_ptr = p as *mut ffi::sqlite3_stmt;
1538 let sql_ptr = x as *const libc::c_char;
1539 if sql_ptr.is_null() {
1540 return;
1541 }
1542 let sql = unsafe { CStr::from_ptr(sql_ptr) }.to_string_lossy();
1545 let readonly =
1546 !stmt_ptr.is_null() && unsafe { ffi::sqlite3_stmt_readonly(stmt_ptr) != 0 };
1547 f(SqliteTraceEvent::Statement {
1548 sql: &sql,
1549 readonly,
1550 });
1551 }
1552 TRACE_PROFILE => {
1553 let stmt_ptr = p as *mut ffi::sqlite3_stmt;
1555 let duration_ns = unsafe {
1556 (x as *const ffi::sqlite3_int64).as_ref()
1558 }
1559 .copied()
1560 .unwrap_or_default()
1561 .cast_unsigned();
1562
1563 let readonly =
1564 !stmt_ptr.is_null() && unsafe { ffi::sqlite3_stmt_readonly(stmt_ptr) != 0 };
1565 let sql_ptr = if stmt_ptr.is_null() {
1566 core::ptr::null()
1567 } else {
1568 unsafe { ffi::sqlite3_sql(stmt_ptr) }
1569 };
1570 let sql = if sql_ptr.is_null() {
1572 Cow::Borrowed("")
1573 } else {
1574 unsafe { CStr::from_ptr(sql_ptr) }.to_string_lossy()
1575 };
1576 f(SqliteTraceEvent::Profile {
1577 sql: &sql,
1578 duration_ns,
1579 readonly,
1580 });
1581 }
1582 TRACE_ROW => f(SqliteTraceEvent::Row),
1583 _ => {}
1587 }
1588 }));
1589
1590 if result.is_err() {
1591 {
::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", 1591u32));
};
crate::util::std_compat::abort();assert_fail!("Panic in sqlite3_trace_v2 trampoline. ");
1592 }
1593
1594 0 }
1596
1597unsafe extern "C" fn collation_needed_trampoline<F>(
1607 user_data: *mut libc::c_void,
1608 db: *mut ffi::sqlite3,
1609 e_text_rep: libc::c_int,
1610 name: *const libc::c_char,
1611) where
1612 F: Fn(&mut SqliteConnection, CollationNeededContext<'_>),
1613{
1614 let result = crate::util::std_compat::catch_unwind(core::panic::AssertUnwindSafe(|| {
1615 let f = unsafe { &*(user_data as *const F) };
1618
1619 let name: Cow<'_, str> = if name.is_null() {
1623 Cow::Borrowed("")
1624 } else {
1625 unsafe { CStr::from_ptr(name) }.to_string_lossy()
1626 };
1627
1628 let Some(db) = NonNull::new(db) else {
1629 return;
1630 };
1631
1632 let ctx = CollationNeededContext {
1633 name: &name,
1634 text_rep: SqliteTextRep::from_ffi(e_text_rep),
1635 };
1636
1637 unsafe {
1641 SqliteConnection::with_borrowed_connection(db, |conn| f(conn, ctx));
1642 }
1643 }));
1644
1645 if result.is_err() {
1646 {
::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", 1646u32));
};
crate::util::std_compat::abort();assert_fail!("Panic in sqlite3_collation_needed trampoline. ");
1647 }
1648}
1649
1650#[cfg(test)]
1651mod tests {
1652 use super::super::update_hook::SqliteChangeOp;
1653 use super::*;
1654 use std::sync::{Arc, Mutex};
1655
1656 fn test_connection() -> RawConnection {
1657 RawConnection::establish(":memory:").expect("failed to establish :memory: connection")
1658 }
1659
1660 #[test]
1661 fn insert_event_dispatched_directly() {
1662 let mut conn = test_connection();
1663 conn.exec("CREATE TABLE t (id INTEGER PRIMARY KEY)")
1664 .unwrap();
1665
1666 let fired = Arc::new(Mutex::new(Vec::new()));
1667 let f2 = fired.clone();
1668 conn.set_update_hook(move |e| {
1669 f2.lock().unwrap().push((e.op, e.rowid));
1670 });
1671
1672 conn.exec("INSERT INTO t VALUES (1)").unwrap();
1673
1674 let events = fired.lock().unwrap();
1675 assert_eq!(events.len(), 1);
1676 assert_eq!(events[0].0, SqliteChangeOp::Insert);
1677 assert_eq!(events[0].1, 1);
1678 }
1679
1680 #[test]
1681 fn consecutive_inserts_dispatch_immediately() {
1682 let mut conn = test_connection();
1683 conn.exec("CREATE TABLE t (id INTEGER PRIMARY KEY)")
1684 .unwrap();
1685
1686 let fired = Arc::new(Mutex::new(Vec::new()));
1687 let f2 = fired.clone();
1688 conn.set_update_hook(move |e| {
1689 f2.lock().unwrap().push(e.rowid);
1690 });
1691
1692 conn.exec("INSERT INTO t VALUES (2); INSERT INTO t VALUES (3)")
1693 .unwrap();
1694
1695 let events = fired.lock().unwrap();
1696 assert_eq!(events.len(), 2);
1697 assert_eq!(events[0], 2);
1698 assert_eq!(events[1], 3);
1699 }
1700
1701 #[test]
1702 fn update_event() {
1703 let mut conn = test_connection();
1704 conn.exec("CREATE TABLE t (id INTEGER PRIMARY KEY, v TEXT)")
1705 .unwrap();
1706 conn.exec("INSERT INTO t VALUES (1, 'a')").unwrap();
1707
1708 let fired = Arc::new(Mutex::new(Vec::new()));
1709 let f2 = fired.clone();
1710 conn.set_update_hook(move |e| {
1711 f2.lock().unwrap().push((e.op, e.rowid));
1712 });
1713
1714 conn.exec("UPDATE t SET v = 'b' WHERE id = 1").unwrap();
1715
1716 let events = fired.lock().unwrap();
1717 assert_eq!(events.len(), 1);
1718 assert_eq!(events[0].0, SqliteChangeOp::Update);
1719 assert_eq!(events[0].1, 1);
1720 }
1721
1722 #[test]
1723 fn delete_event() {
1724 let mut conn = test_connection();
1725 conn.exec("CREATE TABLE t (id INTEGER PRIMARY KEY)")
1726 .unwrap();
1727 conn.exec("INSERT INTO t VALUES (1)").unwrap();
1728
1729 let fired = Arc::new(Mutex::new(Vec::new()));
1730 let f2 = fired.clone();
1731 conn.set_update_hook(move |e| {
1732 f2.lock().unwrap().push((e.op, e.rowid));
1733 });
1734
1735 conn.exec("DELETE FROM t WHERE id = 1").unwrap();
1736
1737 let events = fired.lock().unwrap();
1738 assert_eq!(events.len(), 1);
1739 assert_eq!(events[0].0, SqliteChangeOp::Delete);
1740 assert_eq!(events[0].1, 1);
1741 }
1742
1743 #[test]
1744 fn remove_stops_events() {
1745 let mut conn = test_connection();
1746 conn.exec("CREATE TABLE t (id INTEGER PRIMARY KEY)")
1747 .unwrap();
1748
1749 let fired = Arc::new(Mutex::new(Vec::new()));
1750 let f2 = fired.clone();
1751 conn.set_update_hook(move |e| {
1752 f2.lock().unwrap().push(e.rowid);
1753 });
1754
1755 conn.exec("INSERT INTO t VALUES (1)").unwrap();
1756 assert_eq!(fired.lock().unwrap().len(), 1);
1757
1758 conn.remove_update_hook();
1759 conn.exec("INSERT INTO t VALUES (2)").unwrap();
1760 assert_eq!(fired.lock().unwrap().len(), 1); }
1762
1763 #[test]
1764 fn replacing_hook_drops_old() {
1765 let mut conn = test_connection();
1766 conn.exec("CREATE TABLE t (id INTEGER PRIMARY KEY)")
1767 .unwrap();
1768
1769 let first = Arc::new(Mutex::new(Vec::new()));
1770 let f1 = first.clone();
1771 conn.set_update_hook(move |e| {
1772 f1.lock().unwrap().push(e.rowid);
1773 });
1774
1775 conn.exec("INSERT INTO t VALUES (1)").unwrap();
1776 assert_eq!(first.lock().unwrap().len(), 1);
1777
1778 let second = Arc::new(Mutex::new(Vec::new()));
1780 let f2 = second.clone();
1781 conn.set_update_hook(move |e| {
1782 f2.lock().unwrap().push(e.rowid);
1783 });
1784
1785 conn.exec("INSERT INTO t VALUES (2)").unwrap();
1786 assert_eq!(first.lock().unwrap().len(), 1); assert_eq!(*second.lock().unwrap(), vec![2]);
1788 }
1789
1790 #[test]
1791 fn drop_does_not_panic() {
1792 let mut conn = test_connection();
1793 conn.exec("CREATE TABLE t (id INTEGER PRIMARY KEY)")
1794 .unwrap();
1795
1796 conn.set_update_hook(|_| {});
1797 conn.exec("INSERT INTO t VALUES (1)").unwrap();
1798 drop(conn);
1799 }
1801}