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