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