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