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::functions::{build_sql_function_args, process_sql_function_result};
9use super::serialized_database::SerializedDatabase;
10use super::stmt::ensure_sqlite_ok;
11use super::{Sqlite, SqliteAggregateFunction};
12use crate::deserialize::FromSqlRow;
13use crate::result::Error::DatabaseError;
14use crate::result::*;
15use crate::serialize::ToSql;
16use crate::sql_types::HasSqlType;
17use alloc::borrow::ToOwned;
18use alloc::boxed::Box;
19use alloc::ffi::{CString, NulError};
20use alloc::string::{String, ToString};
21use core::ffi as libc;
22use core::ptr::NonNull;
23use core::{mem, ptr, slice, str};
24
25macro_rules! assert_fail {
28 ($fmt:expr_2021 $(,$args:tt)*) => {
29 #[cfg(feature = "std")]
30 eprint!(concat!(
31 $fmt,
32 "If you see this message, please open an issue at https://github.com/diesel-rs/diesel/issues/new.\n",
33 "Source location: {}:{}\n",
34 ), $($args,)* file!(), line!());
35 crate::util::std_compat::abort()
36 };
37}
38
39#[allow(missing_debug_implementations, missing_copy_implementations)]
40pub(super) struct RawConnection {
41 pub(super) internal_connection: NonNull<ffi::sqlite3>,
42}
43
44impl RawConnection {
45 pub(super) fn establish(database_url: &str) -> ConnectionResult<Self> {
46 let mut conn_pointer = ptr::null_mut();
47
48 let database_url = if database_url.starts_with("sqlite://") {
49 CString::new(database_url.replacen("sqlite://", "file:", 1))?
50 } else {
51 CString::new(database_url)?
52 };
53 let flags = ffi::SQLITE_OPEN_READWRITE | ffi::SQLITE_OPEN_CREATE | ffi::SQLITE_OPEN_URI;
54 let connection_status = unsafe {
55 ffi::sqlite3_open_v2(database_url.as_ptr(), &mut conn_pointer, flags, ptr::null())
56 };
57
58 match connection_status {
59 ffi::SQLITE_OK => {
60 let conn_pointer = unsafe { NonNull::new_unchecked(conn_pointer) };
61 Ok(RawConnection {
62 internal_connection: conn_pointer,
63 })
64 }
65 err_code => {
66 let message = super::error_message(err_code);
67 unsafe { ffi::sqlite3_close(conn_pointer) };
73 Err(ConnectionError::BadConnection(message.into()))
74 }
75 }
76 }
77
78 pub(super) fn exec(&self, query: &str) -> QueryResult<()> {
79 let query = CString::new(query)?;
80 let callback_fn = None;
81 let callback_arg = ptr::null_mut();
82 let result = unsafe {
83 ffi::sqlite3_exec(
84 self.internal_connection.as_ptr(),
85 query.as_ptr(),
86 callback_fn,
87 callback_arg,
88 ptr::null_mut(),
89 )
90 };
91
92 ensure_sqlite_ok(result, self.internal_connection.as_ptr())
93 }
94
95 pub(super) fn rows_affected_by_last_query(
96 &self,
97 ) -> Result<usize, Box<dyn core::error::Error + Send + Sync>> {
98 let r = unsafe { ffi::sqlite3_changes(self.internal_connection.as_ptr()) };
99
100 Ok(r.try_into()?)
101 }
102
103 pub(super) fn last_insert_rowid(&self) -> i64 {
104 unsafe { ffi::sqlite3_last_insert_rowid(self.internal_connection.as_ptr()) }
105 }
106
107 pub(super) fn register_sql_function<F, Ret, RetSqlType>(
108 &self,
109 fn_name: &str,
110 num_args: usize,
111 deterministic: bool,
112 f: F,
113 ) -> QueryResult<()>
114 where
115 F: FnMut(&Self, &mut [*mut ffi::sqlite3_value]) -> QueryResult<Ret>
116 + core::panic::UnwindSafe
117 + Send
118 + 'static,
119 Ret: ToSql<RetSqlType, Sqlite>,
120 Sqlite: HasSqlType<RetSqlType>,
121 {
122 let c_fn_name = Self::get_fn_name(fn_name)?;
123 let flags = Self::get_flags(deterministic);
124 let num_args = num_args
125 .try_into()
126 .map_err(|e| Error::SerializationError(Box::new(e)))?;
127 let callback_fn = Box::into_raw(Box::new(CustomFunctionUserPtr {
130 callback: f,
131 function_name: fn_name.to_owned(),
132 }));
133
134 let result = unsafe {
135 ffi::sqlite3_create_function_v2(
136 self.internal_connection.as_ptr(),
137 c_fn_name.as_ptr(),
138 num_args,
139 flags,
140 callback_fn as *mut _,
141 Some(run_custom_function::<F, Ret, RetSqlType>),
142 None,
143 None,
144 Some(destroy_boxed::<CustomFunctionUserPtr<F>>),
145 )
146 };
147
148 Self::process_sql_function_result(result)
149 }
150
151 pub(super) fn register_aggregate_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
152 &self,
153 fn_name: &str,
154 num_args: usize,
155 ) -> QueryResult<()>
156 where
157 A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send + core::panic::UnwindSafe,
158 Args: FromSqlRow<ArgsSqlType, Sqlite>,
159 Ret: ToSql<RetSqlType, Sqlite>,
160 Sqlite: HasSqlType<RetSqlType>,
161 {
162 let fn_name = Self::get_fn_name(fn_name)?;
163 let flags = Self::get_flags(false);
164 let num_args = num_args
165 .try_into()
166 .map_err(|e| Error::SerializationError(Box::new(e)))?;
167
168 let result = unsafe {
169 ffi::sqlite3_create_function_v2(
170 self.internal_connection.as_ptr(),
171 fn_name.as_ptr(),
172 num_args,
173 flags,
174 core::ptr::null_mut(),
175 None,
176 Some(run_aggregator_step_function::<_, _, _, _, A>),
177 Some(run_aggregator_final_function::<_, _, _, _, A>),
178 None,
179 )
180 };
181
182 Self::process_sql_function_result(result)
183 }
184
185 pub(super) fn register_collation_function<F>(
186 &self,
187 collation_name: &str,
188 collation: F,
189 ) -> QueryResult<()>
190 where
191 F: Fn(&str, &str) -> core::cmp::Ordering + core::panic::UnwindSafe + Send + 'static,
192 {
193 let c_collation_name = Self::get_fn_name(collation_name)?;
194 let callback_fn = Box::into_raw(Box::new(CollationUserPtr {
196 callback: collation,
197 collation_name: collation_name.to_owned(),
198 }));
199
200 let result = unsafe {
201 ffi::sqlite3_create_collation_v2(
202 self.internal_connection.as_ptr(),
203 c_collation_name.as_ptr(),
204 ffi::SQLITE_UTF8,
205 callback_fn as *mut _,
206 Some(run_collation_function::<F>),
207 Some(destroy_boxed::<CollationUserPtr<F>>),
208 )
209 };
210
211 let result = Self::process_sql_function_result(result);
212 if result.is_err() {
213 destroy_boxed::<CollationUserPtr<F>>(callback_fn as *mut _);
214 }
215 result
216 }
217
218 pub(super) fn serialize(&mut self) -> SerializedDatabase {
219 unsafe {
220 let mut size: ffi::sqlite3_int64 = 0;
221 let data_ptr = ffi::sqlite3_serialize(
222 self.internal_connection.as_ptr(),
223 core::ptr::null(),
224 &mut size as *mut _,
225 0,
226 );
227 SerializedDatabase::new(
228 data_ptr,
229 size.try_into()
230 .expect("Cannot fit the serialized database into memory"),
231 )
232 }
233 }
234
235 pub(super) fn deserialize(&mut self, data: &[u8]) -> QueryResult<()> {
236 let db_size = data
237 .len()
238 .try_into()
239 .map_err(|e| Error::DeserializationError(Box::new(e)))?;
240 #[allow(clippy::unnecessary_cast)]
242 unsafe {
243 let result = ffi::sqlite3_deserialize(
244 self.internal_connection.as_ptr(),
245 core::ptr::null(),
246 data.as_ptr() as *mut u8,
247 db_size,
248 db_size,
249 ffi::SQLITE_DESERIALIZE_READONLY as u32,
250 );
251
252 ensure_sqlite_ok(result, self.internal_connection.as_ptr())
253 }
254 }
255
256 fn get_fn_name(fn_name: &str) -> Result<CString, NulError> {
257 CString::new(fn_name)
258 }
259
260 fn get_flags(deterministic: bool) -> i32 {
261 let mut flags = ffi::SQLITE_UTF8;
262 if deterministic {
263 flags |= ffi::SQLITE_DETERMINISTIC;
264 }
265 flags
266 }
267
268 fn process_sql_function_result(result: i32) -> Result<(), Error> {
269 if result == ffi::SQLITE_OK {
270 Ok(())
271 } else {
272 let error_message = super::error_message(result);
273 Err(DatabaseError(
274 DatabaseErrorKind::Unknown,
275 Box::new(error_message.to_string()),
276 ))
277 }
278 }
279
280 pub(super) fn blob_open<'conn>(
281 &'conn self,
282 database_name: &str,
283 table_name: &str,
284 column_name: &str,
285 row_id: i64,
286 ) -> Result<super::sqlite_blob::SqliteReadOnlyBlob<'conn>, Error> {
287 let database_name = alloc::ffi::CString::new(database_name)?;
288 let column_name = alloc::ffi::CString::new(column_name)?;
289 let table_name = alloc::ffi::CString::new(table_name)?;
290
291 let mut blob: *mut ffi::sqlite3_blob = core::ptr::null_mut();
292
293 let ret = unsafe {
295 ffi::sqlite3_blob_open(
296 self.internal_connection.as_ptr(),
297 database_name.as_c_str().as_ptr(),
298 table_name.as_c_str().as_ptr(),
299 column_name.as_c_str().as_ptr(),
300 row_id,
301 0,
302 &mut blob,
303 )
304 };
305
306 Self::process_sql_function_result(ret)?;
307
308 let blob = unsafe { core::ptr::NonNull::new_unchecked(blob) };
316
317 let blob_size = unsafe { ffi::sqlite3_blob_bytes(blob.as_ptr()) };
319 let blob_size = usize::try_from(blob_size).map_err(Error::IntegerConversion)?;
320
321 Ok(super::sqlite_blob::SqliteReadOnlyBlob {
322 blob,
323 read_index: 0,
324 blob_size,
325 _pd: core::marker::PhantomData,
326 })
327 }
328}
329
330impl Drop for RawConnection {
331 fn drop(&mut self) {
332 use crate::util::std_compat::panicking;
333
334 let close_result = unsafe { ffi::sqlite3_close(self.internal_connection.as_ptr()) };
335 if close_result != ffi::SQLITE_OK {
336 let error_message = super::error_message(close_result);
337 if panicking() {
338 #[cfg(feature = "std")]
339 {
::std::io::_eprint(format_args!("Error closing SQLite connection: {0}\n",
error_message));
};eprintln!("Error closing SQLite connection: {error_message}");
340 } else {
341 {
::core::panicking::panic_fmt(format_args!("Error closing SQLite connection: {0}",
error_message));
};panic!("Error closing SQLite connection: {error_message}");
342 }
343 }
344 }
345}
346
347enum SqliteCallbackError {
348 Abort(&'static str),
349 DieselError(crate::result::Error),
350 Panic(String),
351}
352
353impl SqliteCallbackError {
354 fn emit(&self, ctx: *mut ffi::sqlite3_context) {
355 let s;
356 let msg = match self {
357 SqliteCallbackError::Abort(msg) => *msg,
358 SqliteCallbackError::DieselError(e) => {
359 s = e.to_string();
360 &s
361 }
362 SqliteCallbackError::Panic(msg) => msg,
363 };
364 unsafe {
365 context_error_str(ctx, msg);
366 }
367 }
368}
369
370impl From<crate::result::Error> for SqliteCallbackError {
371 fn from(e: crate::result::Error) -> Self {
372 Self::DieselError(e)
373 }
374}
375
376struct CustomFunctionUserPtr<F> {
377 callback: F,
378 function_name: String,
379}
380
381#[allow(warnings)]
382extern "C" fn run_custom_function<F, Ret, RetSqlType>(
383 ctx: *mut ffi::sqlite3_context,
384 num_args: libc::c_int,
385 value_ptr: *mut *mut ffi::sqlite3_value,
386) where
387 F: FnMut(&RawConnection, &mut [*mut ffi::sqlite3_value]) -> QueryResult<Ret>
388 + core::panic::UnwindSafe
389 + Send
390 + 'static,
391 Ret: ToSql<RetSqlType, Sqlite>,
392 Sqlite: HasSqlType<RetSqlType>,
393{
394 use core::ops::Deref;
395 static NULL_DATA_ERR: &str = "An unknown error occurred. sqlite3_user_data returned a null pointer. This should never happen.";
396 static NULL_CONN_ERR: &str = "An unknown error occurred. sqlite3_context_db_handle returned a null pointer. This should never happen.";
397
398 let conn = match unsafe { NonNull::new(ffi::sqlite3_context_db_handle(ctx)) } {
399 Some(conn) => mem::ManuallyDrop::new(RawConnection {
402 internal_connection: conn,
403 }),
404 None => {
405 unsafe { context_error_str(ctx, NULL_CONN_ERR) };
406 return;
407 }
408 };
409
410 let data_ptr = unsafe { ffi::sqlite3_user_data(ctx) };
411
412 let mut data_ptr = match NonNull::new(data_ptr as *mut CustomFunctionUserPtr<F>) {
413 None => unsafe {
414 context_error_str(ctx, NULL_DATA_ERR);
415 return;
416 },
417 Some(mut f) => f,
418 };
419 let data_ptr = unsafe { data_ptr.as_mut() };
420
421 let callback = core::panic::AssertUnwindSafe(&mut data_ptr.callback);
424
425 let result = crate::util::std_compat::catch_unwind(move || {
426 let _ = &callback;
427 let args = unsafe { slice::from_raw_parts_mut(value_ptr, num_args as _) };
428 let res = (callback.0)(&*conn, args)?;
429 let value = process_sql_function_result(&res)?;
430 unsafe {
432 value.result_of(&mut *ctx);
433 }
434 Ok(())
435 })
436 .unwrap_or_else(|p| Err(SqliteCallbackError::Panic(data_ptr.function_name.clone())));
437 if let Err(e) = result {
438 e.emit(ctx);
439 }
440}
441
442#[allow(warnings)]
443extern "C" fn run_aggregator_step_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
444 ctx: *mut ffi::sqlite3_context,
445 num_args: libc::c_int,
446 value_ptr: *mut *mut ffi::sqlite3_value,
447) where
448 A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send + core::panic::UnwindSafe,
449 Args: FromSqlRow<ArgsSqlType, Sqlite>,
450 Ret: ToSql<RetSqlType, Sqlite>,
451 Sqlite: HasSqlType<RetSqlType>,
452{
453 let result = crate::util::std_compat::catch_unwind(move || {
454 let args = unsafe { slice::from_raw_parts_mut(value_ptr, num_args as _) };
455 run_aggregator_step::<A, Args, ArgsSqlType>(ctx, args)
456 })
457 .unwrap_or_else(|e| {
458 Err(SqliteCallbackError::Panic(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::step() panicked",
core::any::type_name::<A>()))
})alloc::format!(
459 "{}::step() panicked",
460 core::any::type_name::<A>()
461 )))
462 });
463
464 match result {
465 Ok(()) => {}
466 Err(e) => e.emit(ctx),
467 }
468}
469
470fn run_aggregator_step<A, Args, ArgsSqlType>(
471 ctx: *mut ffi::sqlite3_context,
472 args: &mut [*mut ffi::sqlite3_value],
473) -> Result<(), SqliteCallbackError>
474where
475 A: SqliteAggregateFunction<Args>,
476 Args: FromSqlRow<ArgsSqlType, Sqlite>,
477{
478 let aggregator = unsafe {
479 const {
480 if core::mem::size_of::<*mut A>() == 0 {
481 {
::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!(
482 "The pointer size is zero, that's unexpected.\
483 If you ever see this error message open a issue\
484 describing your environment"
485 );
486 }
487 }
488 let ctx = ffi::sqlite3_aggregate_context(
495 ctx,
496 core::mem::size_of::<*mut A>()
497 .try_into()
498 .expect("Memory size of a pointer is smaller than i32::MAX"),
499 )
500 .cast::<*mut A>();
502 let inner = &mut *ctx;
504 if inner.is_null() {
508 let obj = Box::into_raw(Box::new(A::default()));
511 *inner = obj;
512 }
513 &mut **inner
517 };
518
519 let args = build_sql_function_args::<ArgsSqlType, Args>(args)?;
520
521 aggregator.step(args);
522 Ok(())
523}
524
525extern "C" fn run_aggregator_final_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
526 ctx: *mut ffi::sqlite3_context,
527) where
528 A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send,
529 Args: FromSqlRow<ArgsSqlType, Sqlite>,
530 Ret: ToSql<RetSqlType, Sqlite>,
531 Sqlite: HasSqlType<RetSqlType>,
532{
533 let result = crate::util::std_compat::catch_unwind(|| {
534 let aggregator = unsafe {
535 let ctx = ffi::sqlite3_aggregate_context(
538 ctx,
539 0,
541 )
542 .cast::<*mut A>();
544 if ctx.is_null() {
548 None
549 } else {
550 let inner = &mut *ctx;
554 if inner.is_null() {
555 None
557 } else {
558 let value = Box::from_raw(*inner);
562 let value = Some(*value);
563 *inner = core::ptr::null_mut();
566 value
567 }
568 }
569 };
570
571 let res = A::finalize(aggregator);
572 let value = process_sql_function_result(&res)?;
573 let r = unsafe { value.result_of(&mut *ctx) };
575 r.map_err(|e| {
576 SqliteCallbackError::DieselError(crate::result::Error::SerializationError(Box::new(e)))
577 })?;
578 Ok(())
579 })
580 .unwrap_or_else(|_e| {
581 Err(SqliteCallbackError::Panic(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::finalize() panicked",
core::any::type_name::<A>()))
})alloc::format!(
582 "{}::finalize() panicked",
583 core::any::type_name::<A>()
584 )))
585 });
586 if let Err(e) = result {
587 e.emit(ctx);
588 }
589}
590
591unsafe fn context_error_str(ctx: *mut ffi::sqlite3_context, error: &str) {
592 let len: i32 = error.len().try_into().unwrap_or(i32::MAX);
593 unsafe {
594 ffi::sqlite3_result_error(ctx, error.as_ptr() as *const _, len);
595 }
596}
597
598struct CollationUserPtr<F> {
599 callback: F,
600 collation_name: String,
601}
602
603#[allow(warnings)]
604extern "C" fn run_collation_function<F>(
605 user_ptr: *mut libc::c_void,
606 lhs_len: libc::c_int,
607 lhs_ptr: *const libc::c_void,
608 rhs_len: libc::c_int,
609 rhs_ptr: *const libc::c_void,
610) -> libc::c_int
611where
612 F: Fn(&str, &str) -> core::cmp::Ordering + Send + core::panic::UnwindSafe + 'static,
613{
614 let user_ptr = user_ptr as *const CollationUserPtr<F>;
615 let user_ptr = core::panic::AssertUnwindSafe(unsafe { user_ptr.as_ref() });
616
617 let result = crate::util::std_compat::catch_unwind(|| {
618 let user_ptr = user_ptr.ok_or_else(|| {
619 SqliteCallbackError::Abort(
620 "Got a null pointer as data pointer. This should never happen",
621 )
622 })?;
623 for (ptr, len, side) in &[(rhs_ptr, rhs_len, "rhs"), (lhs_ptr, lhs_len, "lhs")] {
624 if *len < 0 {
625 {
::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", 625u32));
};
crate::util::std_compat::abort();assert_fail!(
626 "An unknown error occurred. {}_len is negative. This should never happen.",
627 side
628 );
629 }
630 if ptr.is_null() {
631 {
::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", 631u32));
};
crate::util::std_compat::abort();assert_fail!(
632 "An unknown error occurred. {}_ptr is a null pointer. This should never happen.",
633 side
634 );
635 }
636 }
637
638 let (rhs, lhs) = unsafe {
639 (
643 str::from_utf8(slice::from_raw_parts(rhs_ptr as *const u8, rhs_len as _)),
644 str::from_utf8(slice::from_raw_parts(lhs_ptr as *const u8, lhs_len as _)),
645 )
646 };
647
648 let rhs =
649 rhs.map_err(|_| SqliteCallbackError::Abort("Got an invalid UTF-8 string for rhs"))?;
650 let lhs =
651 lhs.map_err(|_| SqliteCallbackError::Abort("Got an invalid UTF-8 string for lhs"))?;
652
653 Ok((user_ptr.callback)(rhs, lhs))
654 })
655 .unwrap_or_else(|p| {
656 Err(SqliteCallbackError::Panic(
657 user_ptr
658 .map(|u| u.collation_name.clone())
659 .unwrap_or_default(),
660 ))
661 });
662
663 match result {
664 Ok(core::cmp::Ordering::Less) => -1,
665 Ok(core::cmp::Ordering::Equal) => 0,
666 Ok(core::cmp::Ordering::Greater) => 1,
667 Err(SqliteCallbackError::Abort(a)) => {
668 #[cfg(feature = "std")]
669 {
::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!(
670 "Collation function {} failed with: {}",
671 user_ptr
672 .map(|c| &c.collation_name as &str)
673 .unwrap_or_default(),
674 a
675 );
676 crate::util::std_compat::abort()
677 }
678 Err(SqliteCallbackError::DieselError(e)) => {
679 #[cfg(feature = "std")]
680 {
::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!(
681 "Collation function {} failed with: {}",
682 user_ptr
683 .map(|c| &c.collation_name as &str)
684 .unwrap_or_default(),
685 e
686 );
687 crate::util::std_compat::abort()
688 }
689 Err(SqliteCallbackError::Panic(msg)) => {
690 #[cfg(feature = "std")]
691 {
::std::io::_eprint(format_args!("Collation function {0} panicked\n",
msg));
};eprintln!("Collation function {} panicked", msg);
692 crate::util::std_compat::abort()
693 }
694 }
695}
696
697extern "C" fn destroy_boxed<F>(data: *mut libc::c_void) {
698 let ptr = data as *mut F;
699 unsafe { core::mem::drop(Box::from_raw(ptr)) };
700}