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 ctx_ptr = Box::into_raw(Box::new(None::<A>));
175
176 let result = unsafe {
177 ffi::sqlite3_create_function_v2(
178 self.internal_connection.as_ptr(),
179 fn_name.as_ptr(),
180 num_args,
181 flags,
182 ctx_ptr.cast(),
183 None,
184 Some(run_aggregator_step_function::<_, _, _, _, A>),
185 Some(run_aggregator_final_function::<_, _, _, _, A>),
186 Some(destroy_boxed::<Option<A>>),
187 )
188 };
189
190 Self::process_sql_function_result(result)
191 }
192
193 pub(super) fn register_collation_function<F>(
194 &self,
195 collation_name: &str,
196 collation: F,
197 ) -> QueryResult<()>
198 where
199 F: Fn(&str, &str) -> core::cmp::Ordering + core::panic::UnwindSafe + Send + 'static,
200 {
201 let c_collation_name = Self::get_fn_name(collation_name)?;
202 let callback_fn = Box::into_raw(Box::new(CollationUserPtr {
204 callback: collation,
205 collation_name: collation_name.to_owned(),
206 }));
207
208 let result = unsafe {
209 ffi::sqlite3_create_collation_v2(
210 self.internal_connection.as_ptr(),
211 c_collation_name.as_ptr(),
212 ffi::SQLITE_UTF8,
213 callback_fn as *mut _,
214 Some(run_collation_function::<F>),
215 Some(destroy_boxed::<CollationUserPtr<F>>),
216 )
217 };
218
219 let result = Self::process_sql_function_result(result);
220 if result.is_err() {
221 destroy_boxed::<CollationUserPtr<F>>(callback_fn as *mut _);
222 }
223 result
224 }
225
226 pub(super) fn serialize(&mut self) -> SerializedDatabase {
227 unsafe {
228 let mut size: ffi::sqlite3_int64 = 0;
229 let data_ptr = ffi::sqlite3_serialize(
230 self.internal_connection.as_ptr(),
231 core::ptr::null(),
232 &mut size as *mut _,
233 0,
234 );
235 SerializedDatabase::new(
236 data_ptr,
237 size.try_into()
238 .expect("Cannot fit the serialized database into memory"),
239 )
240 }
241 }
242
243 pub(super) fn deserialize(&mut self, data: &[u8]) -> QueryResult<()> {
244 let db_size = data
245 .len()
246 .try_into()
247 .map_err(|e| Error::DeserializationError(Box::new(e)))?;
248 #[allow(clippy::unnecessary_cast)]
250 unsafe {
251 let result = ffi::sqlite3_deserialize(
252 self.internal_connection.as_ptr(),
253 core::ptr::null(),
254 data.as_ptr() as *mut u8,
255 db_size,
256 db_size,
257 ffi::SQLITE_DESERIALIZE_READONLY as u32,
258 );
259
260 ensure_sqlite_ok(result, self.internal_connection.as_ptr())
261 }
262 }
263
264 fn get_fn_name(fn_name: &str) -> Result<CString, NulError> {
265 CString::new(fn_name)
266 }
267
268 fn get_flags(deterministic: bool) -> i32 {
269 let mut flags = ffi::SQLITE_UTF8;
270 if deterministic {
271 flags |= ffi::SQLITE_DETERMINISTIC;
272 }
273 flags
274 }
275
276 fn process_sql_function_result(result: i32) -> Result<(), Error> {
277 if result == ffi::SQLITE_OK {
278 Ok(())
279 } else {
280 let error_message = super::error_message(result);
281 Err(DatabaseError(
282 DatabaseErrorKind::Unknown,
283 Box::new(error_message.to_string()),
284 ))
285 }
286 }
287
288 pub(super) fn blob_open<'conn>(
289 &'conn self,
290 database_name: &str,
291 table_name: &str,
292 column_name: &str,
293 row_id: i64,
294 ) -> Result<super::sqlite_blob::SqliteReadOnlyBlob<'conn>, Error> {
295 let database_name = alloc::ffi::CString::new(database_name)?;
296 let column_name = alloc::ffi::CString::new(column_name)?;
297 let table_name = alloc::ffi::CString::new(table_name)?;
298
299 let mut blob: *mut ffi::sqlite3_blob = core::ptr::null_mut();
300
301 let ret = unsafe {
303 ffi::sqlite3_blob_open(
304 self.internal_connection.as_ptr(),
305 database_name.as_c_str().as_ptr(),
306 table_name.as_c_str().as_ptr(),
307 column_name.as_c_str().as_ptr(),
308 row_id,
309 0,
310 &mut blob,
311 )
312 };
313
314 Self::process_sql_function_result(ret)?;
315
316 let blob = unsafe { core::ptr::NonNull::new_unchecked(blob) };
324
325 let blob_size = unsafe { ffi::sqlite3_blob_bytes(blob.as_ptr()) };
327 let blob_size = usize::try_from(blob_size).map_err(Error::IntegerConversion)?;
328
329 Ok(super::sqlite_blob::SqliteReadOnlyBlob {
330 blob,
331 read_index: 0,
332 blob_size,
333 _pd: core::marker::PhantomData,
334 })
335 }
336}
337
338impl Drop for RawConnection {
339 fn drop(&mut self) {
340 use crate::util::std_compat::panicking;
341
342 let close_result = unsafe { ffi::sqlite3_close(self.internal_connection.as_ptr()) };
343 if close_result != ffi::SQLITE_OK {
344 let error_message = super::error_message(close_result);
345 if panicking() {
346 #[cfg(feature = "std")]
347 {
::std::io::_eprint(format_args!("Error closing SQLite connection: {0}\n",
error_message));
};eprintln!("Error closing SQLite connection: {error_message}");
348 } else {
349 {
::core::panicking::panic_fmt(format_args!("Error closing SQLite connection: {0}",
error_message));
};panic!("Error closing SQLite connection: {error_message}");
350 }
351 }
352 }
353}
354
355enum SqliteCallbackError {
356 Abort(&'static str),
357 DieselError(crate::result::Error),
358 Panic(String),
359}
360
361impl SqliteCallbackError {
362 fn emit(&self, ctx: *mut ffi::sqlite3_context) {
363 let s;
364 let msg = match self {
365 SqliteCallbackError::Abort(msg) => *msg,
366 SqliteCallbackError::DieselError(e) => {
367 s = e.to_string();
368 &s
369 }
370 SqliteCallbackError::Panic(msg) => msg,
371 };
372 unsafe {
373 context_error_str(ctx, msg);
374 }
375 }
376}
377
378impl From<crate::result::Error> for SqliteCallbackError {
379 fn from(e: crate::result::Error) -> Self {
380 Self::DieselError(e)
381 }
382}
383
384struct CustomFunctionUserPtr<F> {
385 callback: F,
386 function_name: String,
387}
388
389#[allow(warnings)]
390extern "C" fn run_custom_function<F, Ret, RetSqlType>(
391 ctx: *mut ffi::sqlite3_context,
392 num_args: libc::c_int,
393 value_ptr: *mut *mut ffi::sqlite3_value,
394) where
395 F: FnMut(&RawConnection, &mut [*mut ffi::sqlite3_value]) -> QueryResult<Ret>
396 + core::panic::UnwindSafe
397 + Send
398 + 'static,
399 Ret: ToSql<RetSqlType, Sqlite>,
400 Sqlite: HasSqlType<RetSqlType>,
401{
402 use core::ops::Deref;
403 static NULL_DATA_ERR: &str = "An unknown error occurred. sqlite3_user_data returned a null pointer. This should never happen.";
404 static NULL_CONN_ERR: &str = "An unknown error occurred. sqlite3_context_db_handle returned a null pointer. This should never happen.";
405
406 let conn = match unsafe { NonNull::new(ffi::sqlite3_context_db_handle(ctx)) } {
407 Some(conn) => mem::ManuallyDrop::new(RawConnection {
410 internal_connection: conn,
411 }),
412 None => {
413 unsafe { context_error_str(ctx, NULL_CONN_ERR) };
414 return;
415 }
416 };
417
418 let data_ptr = unsafe { ffi::sqlite3_user_data(ctx) };
419
420 let mut data_ptr = match NonNull::new(data_ptr as *mut CustomFunctionUserPtr<F>) {
421 None => unsafe {
422 context_error_str(ctx, NULL_DATA_ERR);
423 return;
424 },
425 Some(mut f) => f,
426 };
427 let data_ptr = unsafe { data_ptr.as_mut() };
428
429 let callback = core::panic::AssertUnwindSafe(&mut data_ptr.callback);
432
433 let result = crate::util::std_compat::catch_unwind(move || {
434 let _ = &callback;
435 let args = unsafe { slice::from_raw_parts_mut(value_ptr, num_args as _) };
436 let res = (callback.0)(&*conn, args)?;
437 let value = process_sql_function_result(&res)?;
438 unsafe {
440 value.result_of(&mut *ctx);
441 }
442 Ok(())
443 })
444 .unwrap_or_else(|p| Err(SqliteCallbackError::Panic(data_ptr.function_name.clone())));
445 if let Err(e) = result {
446 e.emit(ctx);
447 }
448}
449
450#[allow(warnings)]
451extern "C" fn run_aggregator_step_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
452 ctx: *mut ffi::sqlite3_context,
453 num_args: libc::c_int,
454 value_ptr: *mut *mut ffi::sqlite3_value,
455) where
456 A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send + core::panic::UnwindSafe,
457 Args: FromSqlRow<ArgsSqlType, Sqlite>,
458 Ret: ToSql<RetSqlType, Sqlite>,
459 Sqlite: HasSqlType<RetSqlType>,
460{
461 let result = crate::util::std_compat::catch_unwind(move || {
462 let args = unsafe { slice::from_raw_parts_mut(value_ptr, num_args as _) };
463 run_aggregator_step::<A, Args, ArgsSqlType>(ctx, args)
464 })
465 .unwrap_or_else(|e| {
466 Err(SqliteCallbackError::Panic(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::step() panicked",
core::any::type_name::<A>()))
})alloc::format!(
467 "{}::step() panicked",
468 core::any::type_name::<A>()
469 )))
470 });
471
472 match result {
473 Ok(()) => {}
474 Err(e) => e.emit(ctx),
475 }
476}
477
478fn run_aggregator_step<A, Args, ArgsSqlType>(
479 ctx: *mut ffi::sqlite3_context,
480 args: &mut [*mut ffi::sqlite3_value],
481) -> Result<(), SqliteCallbackError>
482where
483 A: SqliteAggregateFunction<Args>,
484 Args: FromSqlRow<ArgsSqlType, Sqlite>,
485{
486 static NULL_CTX_ERR: &str =
487 "We've written the aggregator to the user data, but it could not be retrieved.";
488
489 let aggregator = unsafe {
490 let aggregate_context = ffi::sqlite3_user_data(ctx) as *mut Option<A>;
494 match aggregate_context.as_mut() {
498 Some(Some(agg)) => agg,
499 Some(r) => {
500 *r = Some(A::default());
501 r.as_mut().expect("Initialised literally above")
502 }
503 None => return Err(SqliteCallbackError::Abort(NULL_CTX_ERR)),
504 }
505 };
506
507 let args = build_sql_function_args::<ArgsSqlType, Args>(args)?;
508
509 aggregator.step(args);
510 Ok(())
511}
512
513extern "C" fn run_aggregator_final_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
514 ctx: *mut ffi::sqlite3_context,
515) where
516 A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send,
517 Args: FromSqlRow<ArgsSqlType, Sqlite>,
518 Ret: ToSql<RetSqlType, Sqlite>,
519 Sqlite: HasSqlType<RetSqlType>,
520{
521 let result = crate::util::std_compat::catch_unwind(|| {
522 let aggregator = unsafe {
523 let aggregate_context = ffi::sqlite3_user_data(ctx) as *mut Option<A>;
527 aggregate_context.as_mut()
531 }
532 .and_then(|a| a.take());
533
534 let res = A::finalize(aggregator);
535 let value = process_sql_function_result(&res)?;
536 let r = unsafe { value.result_of(&mut *ctx) };
538 r.map_err(|e| {
539 SqliteCallbackError::DieselError(crate::result::Error::SerializationError(Box::new(e)))
540 })?;
541 Ok(())
542 })
543 .unwrap_or_else(|_e| {
544 Err(SqliteCallbackError::Panic(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::finalize() panicked",
core::any::type_name::<A>()))
})alloc::format!(
545 "{}::finalize() panicked",
546 core::any::type_name::<A>()
547 )))
548 });
549 if let Err(e) = result {
550 e.emit(ctx);
551 }
552}
553
554unsafe fn context_error_str(ctx: *mut ffi::sqlite3_context, error: &str) {
555 let len: i32 = error.len().try_into().unwrap_or(i32::MAX);
556 unsafe {
557 ffi::sqlite3_result_error(ctx, error.as_ptr() as *const _, len);
558 }
559}
560
561struct CollationUserPtr<F> {
562 callback: F,
563 collation_name: String,
564}
565
566#[allow(warnings)]
567extern "C" fn run_collation_function<F>(
568 user_ptr: *mut libc::c_void,
569 lhs_len: libc::c_int,
570 lhs_ptr: *const libc::c_void,
571 rhs_len: libc::c_int,
572 rhs_ptr: *const libc::c_void,
573) -> libc::c_int
574where
575 F: Fn(&str, &str) -> core::cmp::Ordering + Send + core::panic::UnwindSafe + 'static,
576{
577 let user_ptr = user_ptr as *const CollationUserPtr<F>;
578 let user_ptr = core::panic::AssertUnwindSafe(unsafe { user_ptr.as_ref() });
579
580 let result = crate::util::std_compat::catch_unwind(|| {
581 let user_ptr = user_ptr.ok_or_else(|| {
582 SqliteCallbackError::Abort(
583 "Got a null pointer as data pointer. This should never happen",
584 )
585 })?;
586 for (ptr, len, side) in &[(rhs_ptr, rhs_len, "rhs"), (lhs_ptr, lhs_len, "lhs")] {
587 if *len < 0 {
588 {
::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", 588u32));
};
crate::util::std_compat::abort();assert_fail!(
589 "An unknown error occurred. {}_len is negative. This should never happen.",
590 side
591 );
592 }
593 if ptr.is_null() {
594 {
::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", 594u32));
};
crate::util::std_compat::abort();assert_fail!(
595 "An unknown error occurred. {}_ptr is a null pointer. This should never happen.",
596 side
597 );
598 }
599 }
600
601 let (rhs, lhs) = unsafe {
602 (
606 str::from_utf8(slice::from_raw_parts(rhs_ptr as *const u8, rhs_len as _)),
607 str::from_utf8(slice::from_raw_parts(lhs_ptr as *const u8, lhs_len as _)),
608 )
609 };
610
611 let rhs =
612 rhs.map_err(|_| SqliteCallbackError::Abort("Got an invalid UTF-8 string for rhs"))?;
613 let lhs =
614 lhs.map_err(|_| SqliteCallbackError::Abort("Got an invalid UTF-8 string for lhs"))?;
615
616 Ok((user_ptr.callback)(rhs, lhs))
617 })
618 .unwrap_or_else(|p| {
619 Err(SqliteCallbackError::Panic(
620 user_ptr
621 .map(|u| u.collation_name.clone())
622 .unwrap_or_default(),
623 ))
624 });
625
626 match result {
627 Ok(core::cmp::Ordering::Less) => -1,
628 Ok(core::cmp::Ordering::Equal) => 0,
629 Ok(core::cmp::Ordering::Greater) => 1,
630 Err(SqliteCallbackError::Abort(a)) => {
631 #[cfg(feature = "std")]
632 {
::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!(
633 "Collation function {} failed with: {}",
634 user_ptr
635 .map(|c| &c.collation_name as &str)
636 .unwrap_or_default(),
637 a
638 );
639 crate::util::std_compat::abort()
640 }
641 Err(SqliteCallbackError::DieselError(e)) => {
642 #[cfg(feature = "std")]
643 {
::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!(
644 "Collation function {} failed with: {}",
645 user_ptr
646 .map(|c| &c.collation_name as &str)
647 .unwrap_or_default(),
648 e
649 );
650 crate::util::std_compat::abort()
651 }
652 Err(SqliteCallbackError::Panic(msg)) => {
653 #[cfg(feature = "std")]
654 {
::std::io::_eprint(format_args!("Collation function {0} panicked\n",
msg));
};eprintln!("Collation function {} panicked", msg);
655 crate::util::std_compat::abort()
656 }
657 }
658}
659
660extern "C" fn destroy_boxed<F>(data: *mut libc::c_void) {
661 let ptr = data as *mut F;
662 unsafe { core::mem::drop(Box::from_raw(ptr)) };
663}