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 callback_fn = Box::into_raw(Box::new(CustomFunctionUserPtr {
123 callback: f,
124 function_name: fn_name.to_owned(),
125 }));
126 let fn_name = Self::get_fn_name(fn_name)?;
127 let flags = Self::get_flags(deterministic);
128 let num_args = num_args
129 .try_into()
130 .map_err(|e| Error::SerializationError(Box::new(e)))?;
131
132 let result = unsafe {
133 ffi::sqlite3_create_function_v2(
134 self.internal_connection.as_ptr(),
135 fn_name.as_ptr(),
136 num_args,
137 flags,
138 callback_fn as *mut _,
139 Some(run_custom_function::<F, Ret, RetSqlType>),
140 None,
141 None,
142 Some(destroy_boxed::<CustomFunctionUserPtr<F>>),
143 )
144 };
145
146 Self::process_sql_function_result(result)
147 }
148
149 pub(super) fn register_aggregate_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
150 &self,
151 fn_name: &str,
152 num_args: usize,
153 ) -> QueryResult<()>
154 where
155 A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send + core::panic::UnwindSafe,
156 Args: FromSqlRow<ArgsSqlType, Sqlite>,
157 Ret: ToSql<RetSqlType, Sqlite>,
158 Sqlite: HasSqlType<RetSqlType>,
159 {
160 let fn_name = Self::get_fn_name(fn_name)?;
161 let flags = Self::get_flags(false);
162 let num_args = num_args
163 .try_into()
164 .map_err(|e| Error::SerializationError(Box::new(e)))?;
165
166 let result = unsafe {
167 ffi::sqlite3_create_function_v2(
168 self.internal_connection.as_ptr(),
169 fn_name.as_ptr(),
170 num_args,
171 flags,
172 ptr::null_mut(),
173 None,
174 Some(run_aggregator_step_function::<_, _, _, _, A>),
175 Some(run_aggregator_final_function::<_, _, _, _, A>),
176 None,
177 )
178 };
179
180 Self::process_sql_function_result(result)
181 }
182
183 pub(super) fn register_collation_function<F>(
184 &self,
185 collation_name: &str,
186 collation: F,
187 ) -> QueryResult<()>
188 where
189 F: Fn(&str, &str) -> core::cmp::Ordering + core::panic::UnwindSafe + Send + 'static,
190 {
191 let callback_fn = Box::into_raw(Box::new(CollationUserPtr {
192 callback: collation,
193 collation_name: collation_name.to_owned(),
194 }));
195 let collation_name = Self::get_fn_name(collation_name)?;
196
197 let result = unsafe {
198 ffi::sqlite3_create_collation_v2(
199 self.internal_connection.as_ptr(),
200 collation_name.as_ptr(),
201 ffi::SQLITE_UTF8,
202 callback_fn as *mut _,
203 Some(run_collation_function::<F>),
204 Some(destroy_boxed::<CollationUserPtr<F>>),
205 )
206 };
207
208 let result = Self::process_sql_function_result(result);
209 if result.is_err() {
210 destroy_boxed::<CollationUserPtr<F>>(callback_fn as *mut _);
211 }
212 result
213 }
214
215 pub(super) fn serialize(&mut self) -> SerializedDatabase {
216 unsafe {
217 let mut size: ffi::sqlite3_int64 = 0;
218 let data_ptr = ffi::sqlite3_serialize(
219 self.internal_connection.as_ptr(),
220 core::ptr::null(),
221 &mut size as *mut _,
222 0,
223 );
224 SerializedDatabase::new(
225 data_ptr,
226 size.try_into()
227 .expect("Cannot fit the serialized database into memory"),
228 )
229 }
230 }
231
232 pub(super) fn deserialize(&mut self, data: &[u8]) -> QueryResult<()> {
233 let db_size = data
234 .len()
235 .try_into()
236 .map_err(|e| Error::DeserializationError(Box::new(e)))?;
237 #[allow(clippy::unnecessary_cast)]
239 unsafe {
240 let result = ffi::sqlite3_deserialize(
241 self.internal_connection.as_ptr(),
242 core::ptr::null(),
243 data.as_ptr() as *mut u8,
244 db_size,
245 db_size,
246 ffi::SQLITE_DESERIALIZE_READONLY as u32,
247 );
248
249 ensure_sqlite_ok(result, self.internal_connection.as_ptr())
250 }
251 }
252
253 fn get_fn_name(fn_name: &str) -> Result<CString, NulError> {
254 CString::new(fn_name)
255 }
256
257 fn get_flags(deterministic: bool) -> i32 {
258 let mut flags = ffi::SQLITE_UTF8;
259 if deterministic {
260 flags |= ffi::SQLITE_DETERMINISTIC;
261 }
262 flags
263 }
264
265 fn process_sql_function_result(result: i32) -> Result<(), Error> {
266 if result == ffi::SQLITE_OK {
267 Ok(())
268 } else {
269 let error_message = super::error_message(result);
270 Err(DatabaseError(
271 DatabaseErrorKind::Unknown,
272 Box::new(error_message.to_string()),
273 ))
274 }
275 }
276}
277
278impl Drop for RawConnection {
279 fn drop(&mut self) {
280 use crate::util::std_compat::panicking;
281
282 let close_result = unsafe { ffi::sqlite3_close(self.internal_connection.as_ptr()) };
283 if close_result != ffi::SQLITE_OK {
284 let error_message = super::error_message(close_result);
285 if panicking() {
286 #[cfg(feature = "std")]
287 {
::std::io::_eprint(format_args!("Error closing SQLite connection: {0}\n",
error_message));
};eprintln!("Error closing SQLite connection: {error_message}");
288 } else {
289 {
::core::panicking::panic_fmt(format_args!("Error closing SQLite connection: {0}",
error_message));
};panic!("Error closing SQLite connection: {error_message}");
290 }
291 }
292 }
293}
294
295enum SqliteCallbackError {
296 Abort(&'static str),
297 DieselError(crate::result::Error),
298 Panic(String),
299}
300
301impl SqliteCallbackError {
302 fn emit(&self, ctx: *mut ffi::sqlite3_context) {
303 let s;
304 let msg = match self {
305 SqliteCallbackError::Abort(msg) => *msg,
306 SqliteCallbackError::DieselError(e) => {
307 s = e.to_string();
308 &s
309 }
310 SqliteCallbackError::Panic(msg) => msg,
311 };
312 unsafe {
313 context_error_str(ctx, msg);
314 }
315 }
316}
317
318impl From<crate::result::Error> for SqliteCallbackError {
319 fn from(e: crate::result::Error) -> Self {
320 Self::DieselError(e)
321 }
322}
323
324struct CustomFunctionUserPtr<F> {
325 callback: F,
326 function_name: String,
327}
328
329#[allow(warnings)]
330extern "C" fn run_custom_function<F, Ret, RetSqlType>(
331 ctx: *mut ffi::sqlite3_context,
332 num_args: libc::c_int,
333 value_ptr: *mut *mut ffi::sqlite3_value,
334) where
335 F: FnMut(&RawConnection, &mut [*mut ffi::sqlite3_value]) -> QueryResult<Ret>
336 + core::panic::UnwindSafe
337 + Send
338 + 'static,
339 Ret: ToSql<RetSqlType, Sqlite>,
340 Sqlite: HasSqlType<RetSqlType>,
341{
342 use core::ops::Deref;
343 static NULL_DATA_ERR: &str = "An unknown error occurred. sqlite3_user_data returned a null pointer. This should never happen.";
344 static NULL_CONN_ERR: &str = "An unknown error occurred. sqlite3_context_db_handle returned a null pointer. This should never happen.";
345
346 let conn = match unsafe { NonNull::new(ffi::sqlite3_context_db_handle(ctx)) } {
347 Some(conn) => mem::ManuallyDrop::new(RawConnection {
350 internal_connection: conn,
351 }),
352 None => {
353 unsafe { context_error_str(ctx, NULL_CONN_ERR) };
354 return;
355 }
356 };
357
358 let data_ptr = unsafe { ffi::sqlite3_user_data(ctx) };
359
360 let mut data_ptr = match NonNull::new(data_ptr as *mut CustomFunctionUserPtr<F>) {
361 None => unsafe {
362 context_error_str(ctx, NULL_DATA_ERR);
363 return;
364 },
365 Some(mut f) => f,
366 };
367 let data_ptr = unsafe { data_ptr.as_mut() };
368
369 let callback = core::panic::AssertUnwindSafe(&mut data_ptr.callback);
372
373 let result = crate::util::std_compat::catch_unwind(move || {
374 let _ = &callback;
375 let args = unsafe { slice::from_raw_parts_mut(value_ptr, num_args as _) };
376 let res = (callback.0)(&*conn, args)?;
377 let value = process_sql_function_result(&res)?;
378 unsafe {
380 value.result_of(&mut *ctx);
381 }
382 Ok(())
383 })
384 .unwrap_or_else(|p| Err(SqliteCallbackError::Panic(data_ptr.function_name.clone())));
385 if let Err(e) = result {
386 e.emit(ctx);
387 }
388}
389
390#[repr(u8)]
393enum OptionalAggregator<A> {
394 None,
396 Some(A),
397}
398
399#[allow(warnings)]
400extern "C" fn run_aggregator_step_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
401 ctx: *mut ffi::sqlite3_context,
402 num_args: libc::c_int,
403 value_ptr: *mut *mut ffi::sqlite3_value,
404) where
405 A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send + core::panic::UnwindSafe,
406 Args: FromSqlRow<ArgsSqlType, Sqlite>,
407 Ret: ToSql<RetSqlType, Sqlite>,
408 Sqlite: HasSqlType<RetSqlType>,
409{
410 let result = crate::util::std_compat::catch_unwind(move || {
411 let args = unsafe { slice::from_raw_parts_mut(value_ptr, num_args as _) };
412 run_aggregator_step::<A, Args, ArgsSqlType>(ctx, args)
413 })
414 .unwrap_or_else(|e| {
415 Err(SqliteCallbackError::Panic(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::step() panicked",
core::any::type_name::<A>()))
})alloc::format!(
416 "{}::step() panicked",
417 core::any::type_name::<A>()
418 )))
419 });
420
421 match result {
422 Ok(()) => {}
423 Err(e) => e.emit(ctx),
424 }
425}
426
427fn run_aggregator_step<A, Args, ArgsSqlType>(
428 ctx: *mut ffi::sqlite3_context,
429 args: &mut [*mut ffi::sqlite3_value],
430) -> Result<(), SqliteCallbackError>
431where
432 A: SqliteAggregateFunction<Args>,
433 Args: FromSqlRow<ArgsSqlType, Sqlite>,
434{
435 static NULL_AG_CTX_ERR: &str = "An unknown error occurred. sqlite3_aggregate_context returned a null pointer. This should never happen.";
436 static NULL_CTX_ERR: &str =
437 "We've written the aggregator to the aggregate context, but it could not be retrieved.";
438
439 let n_bytes: i32 = core::mem::size_of::<OptionalAggregator<A>>()
440 .try_into()
441 .expect("Aggregate context should be larger than 2^32");
442 let aggregate_context = unsafe {
443 ffi::sqlite3_aggregate_context(ctx, n_bytes)
465 };
466 let aggregate_context = NonNull::new(aggregate_context as *mut OptionalAggregator<A>);
467 let aggregator = unsafe {
468 match aggregate_context.map(|a| &mut *a.as_ptr()) {
469 Some(&mut OptionalAggregator::Some(ref mut agg)) => agg,
470 Some(a_ptr @ &mut OptionalAggregator::None) => {
471 ptr::write_unaligned(a_ptr as *mut _, OptionalAggregator::Some(A::default()));
472 if let OptionalAggregator::Some(agg) = a_ptr {
473 agg
474 } else {
475 return Err(SqliteCallbackError::Abort(NULL_CTX_ERR));
476 }
477 }
478 None => {
479 return Err(SqliteCallbackError::Abort(NULL_AG_CTX_ERR));
480 }
481 }
482 };
483 let args = build_sql_function_args::<ArgsSqlType, Args>(args)?;
484
485 aggregator.step(args);
486 Ok(())
487}
488
489extern "C" fn run_aggregator_final_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
490 ctx: *mut ffi::sqlite3_context,
491) where
492 A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send,
493 Args: FromSqlRow<ArgsSqlType, Sqlite>,
494 Ret: ToSql<RetSqlType, Sqlite>,
495 Sqlite: HasSqlType<RetSqlType>,
496{
497 static NO_AGGREGATOR_FOUND: &str = "We've written to the aggregator in the xStep callback. If xStep was never called, then ffi::sqlite_aggregate_context() would have returned a NULL pointer.";
498 let aggregate_context = unsafe {
499 ffi::sqlite3_aggregate_context(ctx, 0)
506 };
507
508 let result = crate::util::std_compat::catch_unwind(|| {
509 let mut aggregate_context = NonNull::new(aggregate_context as *mut OptionalAggregator<A>);
510
511 let aggregator = if let Some(a) = aggregate_context.as_mut() {
512 let a = unsafe { a.as_mut() };
513 match core::mem::replace(a, OptionalAggregator::None) {
514 OptionalAggregator::None => {
515 return Err(SqliteCallbackError::Abort(NO_AGGREGATOR_FOUND));
516 }
517 OptionalAggregator::Some(a) => Some(a),
518 }
519 } else {
520 None
521 };
522
523 let res = A::finalize(aggregator);
524 let value = process_sql_function_result(&res)?;
525 let r = unsafe { value.result_of(&mut *ctx) };
527 r.map_err(|e| {
528 SqliteCallbackError::DieselError(crate::result::Error::SerializationError(Box::new(e)))
529 })?;
530 Ok(())
531 })
532 .unwrap_or_else(|_e| {
533 Err(SqliteCallbackError::Panic(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::finalize() panicked",
core::any::type_name::<A>()))
})alloc::format!(
534 "{}::finalize() panicked",
535 core::any::type_name::<A>()
536 )))
537 });
538 if let Err(e) = result {
539 e.emit(ctx);
540 }
541}
542
543unsafe fn context_error_str(ctx: *mut ffi::sqlite3_context, error: &str) {
544 let len: i32 = error
545 .len()
546 .try_into()
547 .expect("Trying to set a error message with more than 2^32 byte is not supported");
548 unsafe {
549 ffi::sqlite3_result_error(ctx, error.as_ptr() as *const _, len);
550 }
551}
552
553struct CollationUserPtr<F> {
554 callback: F,
555 collation_name: String,
556}
557
558#[allow(warnings)]
559extern "C" fn run_collation_function<F>(
560 user_ptr: *mut libc::c_void,
561 lhs_len: libc::c_int,
562 lhs_ptr: *const libc::c_void,
563 rhs_len: libc::c_int,
564 rhs_ptr: *const libc::c_void,
565) -> libc::c_int
566where
567 F: Fn(&str, &str) -> core::cmp::Ordering + Send + core::panic::UnwindSafe + 'static,
568{
569 let user_ptr = user_ptr as *const CollationUserPtr<F>;
570 let user_ptr = core::panic::AssertUnwindSafe(unsafe { user_ptr.as_ref() });
571
572 let result = crate::util::std_compat::catch_unwind(|| {
573 let user_ptr = user_ptr.ok_or_else(|| {
574 SqliteCallbackError::Abort(
575 "Got a null pointer as data pointer. This should never happen",
576 )
577 })?;
578 for (ptr, len, side) in &[(rhs_ptr, rhs_len, "rhs"), (lhs_ptr, lhs_len, "lhs")] {
579 if *len < 0 {
580 {
::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", 580u32));
};
crate::util::std_compat::abort();assert_fail!(
581 "An unknown error occurred. {}_len is negative. This should never happen.",
582 side
583 );
584 }
585 if ptr.is_null() {
586 {
::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", 586u32));
};
crate::util::std_compat::abort();assert_fail!(
587 "An unknown error occurred. {}_ptr is a null pointer. This should never happen.",
588 side
589 );
590 }
591 }
592
593 let (rhs, lhs) = unsafe {
594 (
598 str::from_utf8(slice::from_raw_parts(rhs_ptr as *const u8, rhs_len as _)),
599 str::from_utf8(slice::from_raw_parts(lhs_ptr as *const u8, lhs_len as _)),
600 )
601 };
602
603 let rhs =
604 rhs.map_err(|_| SqliteCallbackError::Abort("Got an invalid UTF-8 string for rhs"))?;
605 let lhs =
606 lhs.map_err(|_| SqliteCallbackError::Abort("Got an invalid UTF-8 string for lhs"))?;
607
608 Ok((user_ptr.callback)(rhs, lhs))
609 })
610 .unwrap_or_else(|p| {
611 Err(SqliteCallbackError::Panic(
612 user_ptr
613 .map(|u| u.collation_name.clone())
614 .unwrap_or_default(),
615 ))
616 });
617
618 match result {
619 Ok(core::cmp::Ordering::Less) => -1,
620 Ok(core::cmp::Ordering::Equal) => 0,
621 Ok(core::cmp::Ordering::Greater) => 1,
622 Err(SqliteCallbackError::Abort(a)) => {
623 #[cfg(feature = "std")]
624 {
::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!(
625 "Collation function {} failed with: {}",
626 user_ptr
627 .map(|c| &c.collation_name as &str)
628 .unwrap_or_default(),
629 a
630 );
631 crate::util::std_compat::abort()
632 }
633 Err(SqliteCallbackError::DieselError(e)) => {
634 #[cfg(feature = "std")]
635 {
::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!(
636 "Collation function {} failed with: {}",
637 user_ptr
638 .map(|c| &c.collation_name as &str)
639 .unwrap_or_default(),
640 e
641 );
642 crate::util::std_compat::abort()
643 }
644 Err(SqliteCallbackError::Panic(msg)) => {
645 #[cfg(feature = "std")]
646 {
::std::io::_eprint(format_args!("Collation function {0} panicked\n",
msg));
};eprintln!("Collation function {} panicked", msg);
647 crate::util::std_compat::abort()
648 }
649 }
650}
651
652extern "C" fn destroy_boxed<F>(data: *mut libc::c_void) {
653 let ptr = data as *mut F;
654 unsafe { core::mem::drop(Box::from_raw(ptr)) };
655}