1#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
2extern crate libsqlite3_sys as ffi;
3
4#[cfg(all(target_family = "wasm", target_os = "unknown"))]
5use sqlite_wasm_rs as ffi;
6
7mod attach;
8pub mod authorizer;
9mod bind_collector;
10mod collation_needed;
11mod db_config;
12mod functions;
13mod hooks;
14mod limits;
15#[cfg(all(
16 test,
17 feature = "std",
18 not(all(target_family = "wasm", target_os = "unknown")),
19 not(miri)
20))]
21#[allow(unsafe_code)]
22mod oom_test_support;
23mod owned_row;
24mod pragmas;
25mod raw;
26mod row;
27mod serialized_database;
28pub(in crate::sqlite) mod sqlite_blob;
29mod sqlite_value;
30mod statement_iterator;
31mod stmt;
32mod trace;
33mod update_hook;
34
35pub use self::authorizer::{AuthorizerContext, AuthorizerDecision};
36pub use self::bind_collector::SqliteBindCollector;#[diesel_derives::__diesel_public_if(
37 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
38)]
39pub(in crate::sqlite) use self::bind_collector::SqliteBindCollector;
40pub use self::bind_collector::SqliteBindValue;
41#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
42pub use self::bind_collector::{OwnedSqliteBindValue, SqliteBindCollectorData, SqliteBindValueRef};
43pub use self::collation_needed::{CollationNeededContext, SqliteTextRep};
44pub use self::limits::SqliteLimit;
45pub use self::pragmas::{AutoVacuumMode, WalCheckpointMode, WalCheckpointOutcome};
46use self::raw::RawConnection;
47pub use self::serialized_database::SerializedDatabase;
48pub use self::sqlite_value::SqliteValue;
49use self::statement_iterator::*;
50use self::stmt::{Statement, StatementUse};
51pub use self::trace::{SqliteTraceEvent, SqliteTraceFlags};
52pub use self::update_hook::{
53 SqliteChangeEvent, SqliteChangeOp, SqliteChangeOps, SqliteUpdateRouter,
54};
55use super::SqliteAggregateFunction;
56use crate::connection::instrumentation::{DynInstrumentation, StrQueryHelper};
57use crate::connection::statement_cache::StatementCache;
58use crate::connection::*;
59use crate::expression::QueryMetadata;
60use crate::query_builder::*;
61use crate::result::*;
62use crate::sql_types::TypeMetadata;
63use crate::sqlite::Sqlite;
64use alloc::string::String;
65use alloc::vec::Vec;
66use core::ffi as libc;
67use core::num::NonZeroI64;
68
69#[allow(missing_debug_implementations)]
201#[cfg(feature = "__sqlite-shared")]
202pub struct SqliteConnection {
203 statement_cache: StatementCache<Sqlite, Statement>,
207 raw_connection: RawConnection,
208 transaction_state: AnsiTransactionManager,
209 metadata_lookup: (),
212 instrumentation: DynInstrumentation,
213 serialized_data: Vec<Vec<u8>>,
224}
225
226#[allow(unsafe_code)]
230unsafe impl Send for SqliteConnection {}
231
232impl SimpleConnection for SqliteConnection {
233 fn batch_execute(&mut self, query: &str) -> QueryResult<()> {
234 self.instrumentation
235 .on_connection_event(InstrumentationEvent::StartQuery {
236 query: &StrQueryHelper::new(query),
237 });
238 let resp = self.raw_connection.exec(query);
239 self.instrumentation
240 .on_connection_event(InstrumentationEvent::FinishQuery {
241 query: &StrQueryHelper::new(query),
242 error: resp.as_ref().err(),
243 });
244 if resp.is_err() && self.raw_connection.is_autocommit() {
245 self.transaction_state.status = TransactionManagerStatus::Valid(Default::default());
247 }
248 resp
249 }
250}
251
252impl ConnectionSealed for SqliteConnection {}
253
254impl Connection for SqliteConnection {
255 type Backend = Sqlite;
256 type TransactionManager = AnsiTransactionManager;
257
258 fn establish(database_url: &str) -> ConnectionResult<Self> {
276 let mut instrumentation = DynInstrumentation::default_instrumentation();
277 instrumentation.on_connection_event(InstrumentationEvent::StartEstablishConnection {
278 url: database_url,
279 });
280
281 let establish_result = Self::establish_inner(database_url);
282 instrumentation.on_connection_event(InstrumentationEvent::FinishEstablishConnection {
283 url: database_url,
284 error: establish_result.as_ref().err(),
285 });
286 let mut conn = establish_result?;
287 conn.instrumentation = instrumentation;
288 Ok(conn)
289 }
290
291 fn execute_returning_count<T>(&mut self, source: &T) -> QueryResult<usize>
292 where
293 T: QueryFragment<Self::Backend> + QueryId,
294 {
295 let statement_use = self.prepared_query(source)?;
296 statement_use.run().and_then(|_| {
297 self.raw_connection
298 .rows_affected_by_last_query()
299 .map_err(Error::DeserializationError)
300 })
301 }
302
303 fn transaction_state(&mut self) -> &mut AnsiTransactionManager
304 where
305 Self: Sized,
306 {
307 &mut self.transaction_state
308 }
309
310 fn instrumentation(&mut self) -> &mut dyn Instrumentation {
311 &mut *self.instrumentation
312 }
313
314 fn set_instrumentation(&mut self, instrumentation: impl Instrumentation) {
315 self.instrumentation = instrumentation.into();
316 }
317
318 fn set_prepared_statement_cache_size(&mut self, size: CacheSize) {
319 self.statement_cache.set_cache_size(size);
320 }
321}
322
323impl LoadConnection<DefaultLoadingMode> for SqliteConnection {
324 type Cursor<'conn, 'query> = StatementIterator<'conn, 'query>;
325 type Row<'conn, 'query> = self::row::SqliteRow<'conn, 'query>;
326
327 fn load<'conn, 'query, T>(
328 &'conn mut self,
329 source: T,
330 ) -> QueryResult<Self::Cursor<'conn, 'query>>
331 where
332 T: Query + QueryFragment<Self::Backend> + QueryId + 'query,
333 Self::Backend: QueryMetadata<T::SqlType>,
334 {
335 let statement = self.prepared_query(source)?;
336
337 Ok(StatementIterator::new(statement))
338 }
339}
340
341impl WithMetadataLookup for SqliteConnection {
342 fn metadata_lookup(&mut self) -> &mut <Sqlite as TypeMetadata>::MetadataLookup {
343 &mut self.metadata_lookup
344 }
345}
346
347#[cfg(feature = "r2d2")]
348impl crate::r2d2::R2D2Connection for crate::sqlite::SqliteConnection {
349 fn ping(&mut self) -> QueryResult<()> {
350 use crate::RunQueryDsl;
351
352 crate::r2d2::CheckConnectionQuery.execute(self).map(|_| ())
353 }
354
355 fn is_broken(&mut self) -> bool {
356 AnsiTransactionManager::is_broken_transaction_manager(self)
357 }
358}
359
360impl MultiConnectionHelper for SqliteConnection {
361 fn to_any<'a>(
362 lookup: &mut <Self::Backend as crate::sql_types::TypeMetadata>::MetadataLookup,
363 ) -> &mut (dyn core::any::Any + 'a) {
364 lookup
365 }
366
367 fn from_any(
368 lookup: &mut dyn core::any::Any,
369 ) -> Option<&mut <Self::Backend as crate::sql_types::TypeMetadata>::MetadataLookup> {
370 lookup.downcast_mut()
371 }
372}
373
374#[derive(#[automatically_derived]impl ::core::fmt::Debug for CommitDecision {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
CommitDecision::Proceed => "Proceed",
CommitDecision::Rollback => "Rollback",
})
}
}Debug, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
for CommitDecision {
}
#[automatically_derived]impl ::core::clone::Clone for CommitDecision {
#[inline]
fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]impl ::core::marker::Copy for CommitDecision { }Copy, #[automatically_derived]impl ::core::marker::StructuralPartialEq for
CommitDecision {
}
#[automatically_derived]impl ::core::cmp::PartialEq for CommitDecision {
#[inline]
fn eq(&self, other: &Self) -> bool {
::core::intrinsics::discriminant_value(self) ==
::core::intrinsics::discriminant_value(other)
}
}PartialEq, #[automatically_derived]impl ::core::cmp::Eq for CommitDecision { }Eq)]
377pub enum CommitDecision {
378 Proceed,
380 Rollback,
382}
383
384#[derive(#[automatically_derived]impl ::core::fmt::Debug for ProgressDecision {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
ProgressDecision::Continue => "Continue",
ProgressDecision::Interrupt => "Interrupt",
})
}
}Debug, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
for ProgressDecision {
}
#[automatically_derived]impl ::core::clone::Clone for ProgressDecision {
#[inline]
fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]impl ::core::marker::Copy for ProgressDecision { }Copy, #[automatically_derived]impl ::core::marker::StructuralPartialEq for
ProgressDecision {
}
#[automatically_derived]impl ::core::cmp::PartialEq for ProgressDecision {
#[inline]
fn eq(&self, other: &Self) -> bool {
::core::intrinsics::discriminant_value(self) ==
::core::intrinsics::discriminant_value(other)
}
}PartialEq, #[automatically_derived]impl ::core::cmp::Eq for ProgressDecision { }Eq)]
387pub enum ProgressDecision {
388 Continue,
390 Interrupt,
392}
393
394#[derive(#[automatically_derived]impl ::core::fmt::Debug for BusyDecision {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
BusyDecision::Retry => "Retry",
BusyDecision::GiveUp => "GiveUp",
})
}
}Debug, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
for BusyDecision {
}
#[automatically_derived]impl ::core::clone::Clone for BusyDecision {
#[inline]
fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]impl ::core::marker::Copy for BusyDecision { }Copy, #[automatically_derived]impl ::core::marker::StructuralPartialEq for
BusyDecision {
}
#[automatically_derived]impl ::core::cmp::PartialEq for BusyDecision {
#[inline]
fn eq(&self, other: &Self) -> bool {
::core::intrinsics::discriminant_value(self) ==
::core::intrinsics::discriminant_value(other)
}
}PartialEq, #[automatically_derived]impl ::core::cmp::Eq for BusyDecision { }Eq)]
397pub enum BusyDecision {
398 Retry,
400 GiveUp,
402}
403
404impl SqliteConnection {
405 pub fn version(&self) -> DatabaseVersion {
419 let version = raw::library_version_number();
420 DatabaseVersion::new(
421 version / 1_000_000,
422 version / 1_000 % 1_000,
423 version % 1_000,
424 )
425 }
426
427 pub fn immediate_transaction<T, E, F>(&mut self, f: F) -> Result<T, E>
449 where
450 F: FnOnce(&mut Self) -> Result<T, E>,
451 E: From<Error>,
452 {
453 self.transaction_sql(f, "BEGIN IMMEDIATE")
454 }
455
456 pub fn exclusive_transaction<T, E, F>(&mut self, f: F) -> Result<T, E>
478 where
479 F: FnOnce(&mut Self) -> Result<T, E>,
480 E: From<Error>,
481 {
482 self.transaction_sql(f, "BEGIN EXCLUSIVE")
483 }
484
485 pub fn last_insert_rowid(&self) -> Option<NonZeroI64> {
521 NonZeroI64::new(self.raw_connection.last_insert_rowid())
522 }
523
524 fn transaction_sql<T, E, F>(&mut self, f: F, sql: &str) -> Result<T, E>
525 where
526 F: FnOnce(&mut Self) -> Result<T, E>,
527 E: From<Error>,
528 {
529 AnsiTransactionManager::begin_transaction_sql(&mut *self, sql)?;
530 match f(&mut *self) {
531 Ok(value) => {
532 AnsiTransactionManager::commit_transaction(&mut *self)?;
533 Ok(value)
534 }
535 Err(e) => {
536 AnsiTransactionManager::rollback_transaction(&mut *self)?;
537 Err(e)
538 }
539 }
540 }
541
542 fn prepared_query<'conn, 'query, T>(
543 &'conn mut self,
544 source: T,
545 ) -> QueryResult<StatementUse<'conn, 'query>>
546 where
547 T: QueryFragment<Sqlite> + QueryId + 'query,
548 {
549 self.instrumentation
550 .on_connection_event(InstrumentationEvent::StartQuery {
551 query: &crate::debug_query(&source),
552 });
553 let raw_connection = &self.raw_connection;
554 let cache = &mut self.statement_cache;
555 let statement = match cache.cached_statement(
556 &source,
557 &Sqlite,
558 &[],
559 raw_connection,
560 Statement::prepare,
561 &mut *self.instrumentation,
562 ) {
563 Ok(statement) => statement,
564 Err(e) => {
565 self.instrumentation
566 .on_connection_event(InstrumentationEvent::FinishQuery {
567 query: &crate::debug_query(&source),
568 error: Some(&e),
569 });
570
571 return Err(e);
572 }
573 };
574
575 StatementUse::bind(statement, source, &mut *self.instrumentation)
576 }
577
578 pub fn serialize_database_to_buffer(&mut self) -> SerializedDatabase {
589 self.raw_connection.serialize()
590 }
591
592 #[allow(unsafe_code)]
632 pub fn deserialize_readonly_database_from_buffer(&mut self, data: &[u8]) -> QueryResult<()> {
633 self.serialized_data.push(data.to_vec());
636 let last = self
637 .serialized_data
638 .last()
639 .expect("We literally pushed it above, so it's there");
640 unsafe {
641 self.raw_connection.deserialize(last)
644 }
645 }
646
647 #[allow(unsafe_code)]
734 pub unsafe fn with_raw_connection<R, F>(&mut self, f: F) -> R
735 where
736 F: FnOnce(*mut ffi::sqlite3) -> R,
737 {
738 f(self.raw_connection.internal_connection.as_ptr())
739 }
740
741 #[allow(unsafe_code)]
750 pub(crate) unsafe fn with_borrowed_connection<R>(
751 db: core::ptr::NonNull<ffi::sqlite3>,
752 f: impl FnOnce(&mut SqliteConnection) -> R,
753 ) -> R {
754 struct Borrowed(core::mem::ManuallyDrop<SqliteConnection>);
757
758 impl Drop for Borrowed {
759 fn drop(&mut self) {
760 let conn = unsafe { core::mem::ManuallyDrop::take(&mut self.0) };
762 let SqliteConnection {
763 statement_cache,
764 raw_connection,
765 ..
766 } = conn;
767 drop(statement_cache);
770 core::mem::forget(raw_connection);
771 }
772 }
773
774 let mut conn = Borrowed(core::mem::ManuallyDrop::new(SqliteConnection {
775 statement_cache: StatementCache::new(),
776 raw_connection: RawConnection::from_ptr(db),
777 transaction_state: AnsiTransactionManager::default(),
778 metadata_lookup: (),
779 instrumentation: DynInstrumentation::default_instrumentation(),
780 serialized_data: Vec::new(),
781 }));
782
783 let result = f(&mut conn.0);
784
785 if true {
if !#[allow(non_exhaustive_omitted_patterns)] match AnsiTransactionManager::transaction_manager_status_mut(&mut *conn.0).transaction_depth()
{
Ok(None) => true,
_ => false,
} {
{
::core::panicking::panic_fmt(format_args!("callback must not leave an open transaction on the borrowed connection"));
}
};
};debug_assert!(
788 matches!(
789 AnsiTransactionManager::transaction_manager_status_mut(&mut *conn.0)
790 .transaction_depth(),
791 Ok(None)
792 ),
793 "callback must not leave an open transaction on the borrowed connection"
794 );
795
796 result
797 }
798
799 fn register_diesel_sql_functions(&self) -> QueryResult<()> {
800 #[cfg(miri)]
808 return Ok(());
809
810 #[cfg(not(miri))]
811 {
812 use crate::sql_types::{Integer, Text};
813
814 self.raw_connection
818 .register_sql_function::<_, i32, Integer>(
819 "diesel_manage_updated_at",
820 1,
821 crate::sqlite::SqliteFunctionBehavior::DIRECTONLY,
822 |conn, args| {
823 let table_name: String = functions::build_sql_function_args::<Text, _>(
824 args,
825 conn.internal_connection,
826 )?;
827 conn.exec(&::alloc::__export::must_use({
::alloc::fmt::format(format_args!("CREATE TRIGGER __diesel_manage_updated_at_{0}\nAFTER UPDATE ON {0}\nFOR EACH ROW WHEN\n old.updated_at IS NULL AND\n new.updated_at IS NULL OR\n old.updated_at == new.updated_at\nBEGIN\n UPDATE {0}\n SET updated_at = CURRENT_TIMESTAMP\n WHERE ROWID = new.ROWID;\nEND\n",
table_name))
})alloc::format!(
828 include_str!("diesel_manage_updated_at.sql"),
829 table_name = table_name
830 ))?;
831 Ok(0) },
833 )
834 }
835 }
836
837 fn establish_inner(database_url: &str) -> Result<SqliteConnection, ConnectionError> {
838 use crate::result::ConnectionError::CouldntSetupConfiguration;
839 let raw_connection = RawConnection::establish(database_url)?;
840 let conn = Self {
841 statement_cache: StatementCache::new(),
842 raw_connection,
843 transaction_state: AnsiTransactionManager::default(),
844 metadata_lookup: (),
845 instrumentation: DynInstrumentation::none(),
846 serialized_data: Vec::new(),
847 };
848 conn.register_diesel_sql_functions()
849 .map_err(CouldntSetupConfiguration)?;
850 Ok(conn)
851 }
852}
853
854fn error_message(err_code: libc::c_int) -> &'static str {
855 ffi::code_to_str(err_code)
856}
857
858#[cfg(test)]
859mod tests {
860 use super::*;
861 use crate::dsl::sql;
862 use crate::prelude::*;
863 use crate::sql_types::{Integer, Text};
864 #[cfg(not(miri))]
865 use crate::test_helpers::format_error;
866
867 fn connection() -> SqliteConnection {
868 SqliteConnection::establish(":memory:").unwrap()
869 }
870
871 #[diesel_test_helper::test]
872 #[allow(unsafe_code)]
873 fn with_raw_connection_can_return_values() {
874 let connection = &mut connection();
875
876 let autocommit_status = unsafe {
878 connection.with_raw_connection(|raw_conn| ffi::sqlite3_get_autocommit(raw_conn))
879 };
880
881 assert_ne!(autocommit_status, 0, "Expected autocommit to be enabled");
883 }
884
885 #[diesel_test_helper::test]
886 #[allow(unsafe_code)]
887 fn with_raw_connection_works_after_diesel_operations() {
888 let connection = &mut connection();
889
890 crate::sql_query("CREATE TABLE test_table (id INTEGER PRIMARY KEY, value TEXT)")
892 .execute(connection)
893 .unwrap();
894 crate::sql_query("INSERT INTO test_table (value) VALUES ('hello')")
895 .execute(connection)
896 .unwrap();
897
898 let last_rowid = unsafe {
900 connection.with_raw_connection(|raw_conn| ffi::sqlite3_last_insert_rowid(raw_conn))
901 };
902
903 assert_eq!(last_rowid, 1, "Last insert rowid should be 1");
904
905 let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM test_table")
907 .get_result(connection)
908 .unwrap();
909 assert_eq!(count, 1);
910 }
911
912 #[diesel_test_helper::test]
913 #[cfg(not(miri))] #[allow(unsafe_code)]
918 fn with_raw_connection_can_execute_raw_sql() {
919 let connection = &mut connection();
920
921 crate::sql_query("CREATE TABLE raw_test (id INTEGER PRIMARY KEY, name TEXT)")
923 .execute(connection)
924 .unwrap();
925
926 let result = unsafe {
929 connection.with_raw_connection(|raw_conn| {
930 let sql = c"INSERT INTO raw_test (name) VALUES ('from_raw')";
931 let mut err_msg: *mut libc::c_char = core::ptr::null_mut();
932 let rc = ffi::sqlite3_exec(
933 raw_conn,
934 sql.as_ptr(),
935 None,
936 core::ptr::null_mut(),
937 &mut err_msg,
938 );
939 if rc != ffi::SQLITE_OK && !err_msg.is_null() {
940 ffi::sqlite3_free(err_msg as *mut libc::c_void);
941 }
942 rc
943 })
944 };
945
946 assert_eq!(result, ffi::SQLITE_OK, "Raw SQL execution should succeed");
947
948 let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM raw_test")
950 .get_result(connection)
951 .unwrap();
952 assert_eq!(count, 1);
953
954 let name: String = sql::<Text>("SELECT name FROM raw_test WHERE id = 1")
955 .get_result(connection)
956 .unwrap();
957 assert_eq!(name, "from_raw");
958 }
959
960 #[diesel_test_helper::test]
961 #[allow(unsafe_code)]
962 fn with_raw_connection_works_within_transaction() {
963 let connection = &mut connection();
964
965 crate::sql_query("CREATE TABLE txn_test (id INTEGER PRIMARY KEY, value INTEGER)")
966 .execute(connection)
967 .unwrap();
968
969 connection
970 .transaction::<_, crate::result::Error, _>(|conn| {
971 crate::sql_query("INSERT INTO txn_test (value) VALUES (42)")
972 .execute(conn)
973 .unwrap();
974
975 let autocommit = unsafe {
977 conn.with_raw_connection(|raw_conn| ffi::sqlite3_get_autocommit(raw_conn))
978 };
979
980 assert_eq!(
982 autocommit, 0,
983 "Autocommit should be disabled inside transaction"
984 );
985
986 Ok(())
987 })
988 .unwrap();
989
990 let autocommit = unsafe {
992 connection.with_raw_connection(|raw_conn| ffi::sqlite3_get_autocommit(raw_conn))
993 };
994 assert_ne!(
995 autocommit, 0,
996 "Autocommit should be enabled after transaction"
997 );
998 }
999
1000 #[diesel_test_helper::test]
1001 #[cfg(not(miri))] #[allow(unsafe_code)]
1006 fn with_raw_connection_can_read_database_filename() {
1007 let connection = &mut connection();
1008
1009 let filename = unsafe {
1011 connection.with_raw_connection(|raw_conn| {
1012 let db_name = c"main";
1013 let filename_ptr = ffi::sqlite3_db_filename(raw_conn, db_name.as_ptr());
1014 if filename_ptr.is_null() {
1015 None
1016 } else {
1017 let cstr = core::ffi::CStr::from_ptr(filename_ptr);
1019 Some(cstr.to_string_lossy().into_owned())
1020 }
1021 })
1022 };
1023
1024 assert_eq!(
1027 filename,
1028 Some(String::new()),
1029 "In-memory database filename should be an empty string"
1030 );
1031 }
1032
1033 #[diesel_test_helper::test]
1034 #[allow(unsafe_code)]
1035 fn with_raw_connection_changes_count() {
1036 let connection = &mut connection();
1037
1038 crate::sql_query("CREATE TABLE changes_test (id INTEGER PRIMARY KEY, value INTEGER)")
1039 .execute(connection)
1040 .unwrap();
1041
1042 crate::sql_query("INSERT INTO changes_test (value) VALUES (1), (2), (3)")
1043 .execute(connection)
1044 .unwrap();
1045
1046 let changes = unsafe {
1048 connection.with_raw_connection(|raw_conn| {
1049 let sql = c"UPDATE changes_test SET value = value + 10";
1050 let mut err_msg: *mut libc::c_char = core::ptr::null_mut();
1051 let rc = ffi::sqlite3_exec(
1052 raw_conn,
1053 sql.as_ptr(),
1054 None,
1055 core::ptr::null_mut(),
1056 &mut err_msg,
1057 );
1058 if rc != ffi::SQLITE_OK && !err_msg.is_null() {
1059 ffi::sqlite3_free(err_msg as *mut libc::c_void);
1060 return -1;
1061 }
1062 ffi::sqlite3_changes(raw_conn)
1063 })
1064 };
1065
1066 assert_eq!(changes, 3, "Should have updated 3 rows");
1067
1068 let values: Vec<i32> = sql::<Integer>("SELECT value FROM changes_test ORDER BY id")
1070 .load(connection)
1071 .unwrap();
1072 assert_eq!(values, vec![11, 12, 13]);
1073 }
1074
1075 #[diesel_test_helper::test]
1077 #[allow(unsafe_code)]
1078 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
1079 fn with_raw_connection_recovers_after_panic() {
1080 let connection = &mut connection();
1081
1082 crate::sql_query("CREATE TABLE panic_test (id INTEGER PRIMARY KEY, value TEXT)")
1083 .execute(connection)
1084 .unwrap();
1085
1086 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
1088 connection.with_raw_connection(|_raw_conn| {
1089 panic!("intentional panic inside with_raw_connection");
1090 })
1091 }));
1092 assert!(result.is_err(), "Should have caught the panic");
1093
1094 crate::sql_query("INSERT INTO panic_test (value) VALUES ('after_panic')")
1096 .execute(connection)
1097 .unwrap();
1098
1099 let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM panic_test")
1100 .get_result(connection)
1101 .unwrap();
1102 assert_eq!(count, 1, "Connection should work after panic in callback");
1103 }
1104
1105 #[diesel_test_helper::test]
1107 #[allow(unsafe_code)]
1108 #[cfg(not(miri))] #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
1113 fn with_raw_connection_can_read_file_database_filename() {
1114 let dir = std::env::temp_dir().join("diesel_test_filename.db");
1115 let db_path = dir.to_str().unwrap();
1116
1117 let _ = std::fs::remove_file(db_path);
1119
1120 let connection = &mut SqliteConnection::establish(db_path).unwrap();
1121
1122 let filename = unsafe {
1124 connection.with_raw_connection(|raw_conn| {
1125 let db_name = c"main";
1126 let filename_ptr = ffi::sqlite3_db_filename(raw_conn, db_name.as_ptr());
1127 if filename_ptr.is_null() {
1128 None
1129 } else {
1130 let cstr = core::ffi::CStr::from_ptr(filename_ptr);
1131 Some(cstr.to_string_lossy().into_owned())
1132 }
1133 })
1134 };
1135
1136 let filename = filename.expect("File-based database should have a filename");
1137 assert!(
1138 filename.contains("diesel_test_filename.db"),
1139 "Filename should contain the database name, got: {filename}"
1140 );
1141
1142 let _ = std::fs::remove_file(db_path);
1144 }
1145
1146 #[cfg(not(miri))] #[diesel_test_helper::test]
1151 fn database_serializes_and_deserializes_successfully() {
1152 let expected_users = vec![
1153 (
1154 1,
1155 "John Doe".to_string(),
1156 "john.doe@example.com".to_string(),
1157 ),
1158 (
1159 2,
1160 "Jane Doe".to_string(),
1161 "jane.doe@example.com".to_string(),
1162 ),
1163 ];
1164
1165 let conn1 = &mut connection();
1166 let _ =
1167 crate::sql_query("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)")
1168 .execute(conn1);
1169 let _ = crate::sql_query("INSERT INTO users (name, email) VALUES ('John Doe', 'john.doe@example.com'), ('Jane Doe', 'jane.doe@example.com')")
1170 .execute(conn1);
1171
1172 for _i in 0..2 {
1173 let serialized_database = conn1.serialize_database_to_buffer();
1174 let conn2 = &mut connection();
1175 conn2
1176 .deserialize_readonly_database_from_buffer(
1177 serialized_database.try_as_slice().unwrap(),
1178 )
1179 .unwrap();
1180
1181 let query =
1182 sql::<(Integer, Text, Text)>("SELECT id, name, email FROM users ORDER BY id");
1183 let actual_users = query.load::<(i32, String, String)>(conn2).unwrap();
1184
1185 assert_eq!(expected_users, actual_users);
1186 std::mem::drop(serialized_database);
1190 let query =
1191 sql::<(Integer, Text, Text)>("SELECT id, name, email FROM users ORDER BY id");
1192 let actual_users = query.load::<(i32, String, String)>(conn2).unwrap();
1193
1194 assert_eq!(expected_users, actual_users);
1195 }
1196 }
1197
1198 #[cfg(not(miri))] #[diesel_test_helper::test]
1203 fn database_deserialize_random_bytes() {
1204 let buffer = vec![0, 1, 2, 3, 4];
1205 let conn = &mut SqliteConnection::establish(":memory:").unwrap();
1206
1207 conn.deserialize_readonly_database_from_buffer(&buffer)
1208 .unwrap();
1209
1210 let r = sql::<Integer>("SELECT id FROM users").load::<i32>(conn);
1211
1212 assert!(r.is_err());
1213 assert_eq!(format_error(&r.unwrap_err()), "file is not a database");
1214
1215 let conn = &mut SqliteConnection::establish(":memory:").unwrap();
1216
1217 let _ =
1218 crate::sql_query("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)")
1219 .execute(conn);
1220 let _ = crate::sql_query("INSERT INTO users (name, email) VALUES ('John Doe', 'john.doe@example.com'), ('Jane Doe', 'jane.doe@example.com')")
1221 .execute(conn);
1222
1223 let db = conn.serialize_database_to_buffer();
1224 let mut bad_buffer = db[..100].to_vec();
1226 bad_buffer.extend(b"whatever");
1227 conn.deserialize_readonly_database_from_buffer(&bad_buffer)
1228 .unwrap();
1229
1230 let r = sql::<Integer>("SELECT id FROM users").load::<i32>(conn);
1231
1232 assert!(r.is_err());
1233 assert_eq!(
1234 format_error(&r.unwrap_err()),
1235 "database disk image is malformed"
1236 );
1237
1238 let mut size_fitting_bad_buffer = db[..100].to_vec();
1240 size_fitting_bad_buffer.extend(
1241 core::iter::repeat(b"abcdefghij")
1242 .flatten()
1243 .take(db.len() - 100),
1244 );
1245 let r = conn.deserialize_readonly_database_from_buffer(&size_fitting_bad_buffer);
1246
1247 assert!(r.is_err());
1248 assert_eq!(
1249 format_error(&r.unwrap_err()),
1250 "database disk image is malformed"
1251 );
1252 }
1253
1254 #[diesel_test_helper::test]
1255 fn database_serializes_empty_deserialized_database() {
1256 let conn = &mut SqliteConnection::establish(":memory:").unwrap();
1257 conn.deserialize_readonly_database_from_buffer(&[]).unwrap();
1258
1259 let serialized = conn.serialize_database_to_buffer();
1260
1261 assert!(serialized.is_empty());
1262 assert!(serialized.try_as_slice().unwrap().is_empty());
1263 }
1264
1265 #[cfg(all(
1266 feature = "std",
1267 not(all(target_family = "wasm", target_os = "unknown")),
1268 not(miri)
1272 ))]
1273 #[allow(unsafe_code)]
1274 mod sqlite_serialize_oom {
1275 use super::super::oom_test_support::{panic_message, run_in_child, with_heap_limit};
1276 use super::super::{SerializedDatabase, ffi};
1277 use crate::connection::{Connection, SimpleConnection};
1278 use crate::sqlite::SqliteConnection;
1279 use crate::test_helpers::format_error;
1280
1281 const MIN_DATABASE_BYTES: i64 = 1_048_576;
1282
1283 fn with_failing_serialize<R>(f: impl FnOnce() -> R) -> R {
1286 with_heap_limit(65_536, f)
1287 }
1288
1289 #[test]
1290 fn sqlite_serialize_oom_is_contained() {
1291 run_in_child(|| {
1292 let mut conn = large_database();
1293
1294 let (baseline_size, baseline) = serialize_direct(&conn);
1295 assert!(
1296 baseline_size >= MIN_DATABASE_BYTES,
1297 "the serialized database is smaller than 1 MiB"
1298 );
1299 assert!(
1300 !baseline.is_null(),
1301 "SQLite refused to serialize a valid database"
1302 );
1303 unsafe { ffi::sqlite3_free(baseline as _) };
1305
1306 let (reported_size, data) = with_failing_serialize(|| serialize_direct(&conn));
1307 if !data.is_null() {
1308 unsafe { ffi::sqlite3_free(data as _) };
1310 }
1311 assert!(
1312 data.is_null(),
1313 "SQLite did not fail the output allocation of the serialization"
1314 );
1315 assert!(
1317 reported_size >= MIN_DATABASE_BYTES,
1318 "SQLite reported a serialization size of {reported_size} with a null buffer"
1319 );
1320
1321 let serialized: SerializedDatabase =
1322 with_failing_serialize(|| conn.serialize_database_to_buffer());
1323 let error = serialized
1324 .try_as_slice()
1325 .expect_err("the failed output allocation must surface as an error");
1326 assert_eq!(format_error(&error), "out of memory");
1327
1328 let payload = std::panic::catch_unwind(core::panic::AssertUnwindSafe(|| {
1329 core::hint::black_box(serialized[0]);
1330 }))
1331 .expect_err("the serialized database access did not panic");
1332 let message = panic_message(&*payload);
1333 assert!(
1334 message.contains("Cannot access the serialized database: out of memory"),
1335 "SQLite serialization allocation failure surfaced as `{message}` instead \
1336 of a caught allocation panic"
1337 );
1338 });
1339 }
1340
1341 fn large_database() -> SqliteConnection {
1342 let mut conn = SqliteConnection::establish(":memory:").unwrap();
1343 conn.batch_execute(&format!(
1344 "CREATE TABLE blobs (id INTEGER PRIMARY KEY, payload BLOB);
1345 INSERT INTO blobs (payload) VALUES (zeroblob({MIN_DATABASE_BYTES}));"
1346 ))
1347 .unwrap();
1348 conn
1349 }
1350
1351 fn serialize_direct(conn: &SqliteConnection) -> (ffi::sqlite3_int64, *mut u8) {
1352 unsafe {
1354 let mut size: ffi::sqlite3_int64 = 0;
1355 let data = ffi::sqlite3_serialize(
1356 conn.raw_connection.internal_connection.as_ptr(),
1357 core::ptr::null(),
1358 &mut size as *mut _,
1359 0,
1360 );
1361 (size, data)
1362 }
1363 }
1364 }
1365
1366 #[cfg(all(
1367 feature = "std",
1368 not(all(target_family = "wasm", target_os = "unknown")),
1369 not(miri)
1370 ))]
1371 mod diesel_manage_updated_at_errors {
1372 use super::super::oom_test_support::run_in_child;
1373 use crate::connection::{Connection, SimpleConnection};
1374 use crate::query_dsl::RunQueryDsl;
1375 use crate::sqlite::{AuthorizerContext, AuthorizerDecision, SqliteConnection};
1376 use crate::test_helpers::format_error;
1377
1378 #[track_caller]
1379 fn assert_no_panic_output(stderr: &str, table_name: &str) {
1380 assert!(
1381 !stderr.contains("panicked") && !stderr.contains(table_name),
1382 "diesel_manage_updated_at reached the panic hook\nstderr:\n{stderr}"
1383 );
1384 }
1385
1386 #[test]
1387 fn missing_table_returns_error_without_panicking() {
1388 let stderr = run_in_child(|| {
1389 let mut conn = SqliteConnection::establish(":memory:").unwrap();
1390 let error = conn
1391 .batch_execute("SELECT diesel_manage_updated_at('synthetic_key_7c91a2')")
1392 .unwrap_err();
1393 assert_eq!(
1394 format_error(&error),
1395 "no such table: main.synthetic_key_7c91a2"
1396 );
1397 });
1398 assert_no_panic_output(&stderr, "synthetic_key_7c91a2");
1399 }
1400
1401 #[test]
1402 fn denied_trigger_returns_error_without_panicking() {
1403 let stderr = run_in_child(|| {
1404 let mut conn = SqliteConnection::establish(":memory:").unwrap();
1405 conn.batch_execute("CREATE TABLE guarded_7c91a2 (id INTEGER, updated_at TEXT)")
1406 .unwrap();
1407 conn.on_authorize(|ctx| match ctx {
1408 AuthorizerContext::CreateTrigger(_) => AuthorizerDecision::Deny,
1409 _ => AuthorizerDecision::Allow,
1410 });
1411 let error = conn
1412 .batch_execute("SELECT diesel_manage_updated_at('guarded_7c91a2')")
1413 .unwrap_err();
1414 assert_eq!(format_error(&error), "not authorized");
1415
1416 conn.remove_authorizer();
1417 let triggers: i64 = crate::dsl::sql::<crate::sql_types::BigInt>(
1418 "SELECT COUNT(*) FROM sqlite_master WHERE type = 'trigger'",
1419 )
1420 .get_result(&mut conn)
1421 .unwrap();
1422 assert_eq!(triggers, 0);
1423 });
1424 assert_no_panic_output(&stderr, "guarded_7c91a2");
1425 }
1426 }
1427
1428 #[diesel_test_helper::test]
1430 fn test_correct_serialization_of_owned_strings() {
1431 use crate::prelude::*;
1432
1433 #[derive(Debug, crate::expression::AsExpression)]
1434 #[diesel(sql_type = diesel::sql_types::Text)]
1435 struct CustomWrapper(String);
1436
1437 impl crate::serialize::ToSql<Text, Sqlite> for CustomWrapper {
1438 fn to_sql<'b>(
1439 &'b self,
1440 out: &mut crate::serialize::Output<'b, '_, Sqlite>,
1441 ) -> crate::serialize::Result {
1442 out.set_value(self.0.to_string());
1443 Ok(crate::serialize::IsNull::No)
1444 }
1445 }
1446
1447 let connection = &mut connection();
1448
1449 let res = crate::select(
1450 CustomWrapper("".into())
1451 .into_sql::<crate::sql_types::Text>()
1452 .nullable(),
1453 )
1454 .get_result::<Option<String>>(connection)
1455 .unwrap();
1456 assert_eq!(res, Some(String::new()));
1457 }
1458
1459 #[diesel_test_helper::test]
1460 fn test_correct_serialization_of_owned_bytes() {
1461 use crate::prelude::*;
1462
1463 #[derive(Debug, crate::expression::AsExpression)]
1464 #[diesel(sql_type = diesel::sql_types::Binary)]
1465 struct CustomWrapper(Vec<u8>);
1466
1467 impl crate::serialize::ToSql<crate::sql_types::Binary, Sqlite> for CustomWrapper {
1468 fn to_sql<'b>(
1469 &'b self,
1470 out: &mut crate::serialize::Output<'b, '_, Sqlite>,
1471 ) -> crate::serialize::Result {
1472 out.set_value(self.0.clone());
1473 Ok(crate::serialize::IsNull::No)
1474 }
1475 }
1476
1477 let connection = &mut connection();
1478
1479 let res = crate::select(
1480 CustomWrapper(Vec::new())
1481 .into_sql::<crate::sql_types::Binary>()
1482 .nullable(),
1483 )
1484 .get_result::<Option<Vec<u8>>>(connection)
1485 .unwrap();
1486 assert_eq!(res, Some(Vec::new()));
1487 }
1488
1489 #[diesel_test_helper::test]
1490 fn correctly_handle_empty_query() {
1491 let check_empty_query_error = |r: crate::QueryResult<usize>| {
1492 assert!(r.is_err());
1493 let err = r.unwrap_err();
1494 assert!(
1495 matches!(err, crate::result::Error::QueryBuilderError(ref b) if b.is::<crate::result::EmptyQuery>()),
1496 "Expected a query builder error, but got {err}"
1497 );
1498 };
1499 let connection = &mut SqliteConnection::establish(":memory:").unwrap();
1500 check_empty_query_error(crate::sql_query("").execute(connection));
1501 check_empty_query_error(crate::sql_query(" ").execute(connection));
1502 check_empty_query_error(crate::sql_query("\n\t").execute(connection));
1503 check_empty_query_error(crate::sql_query("-- SELECT 1;").execute(connection));
1504 }
1505
1506 #[diesel_test_helper::test]
1507 fn last_insert_rowid_returns_none_on_fresh_connection() {
1508 let conn = &mut connection();
1509 assert_eq!(conn.last_insert_rowid(), None);
1510 }
1511
1512 #[diesel_test_helper::test]
1513 fn last_insert_rowid_returns_rowid_after_insert() {
1514 let conn = &mut connection();
1515 crate::sql_query("CREATE TABLE li_test (id INTEGER PRIMARY KEY, val TEXT NOT NULL)")
1516 .execute(conn)
1517 .unwrap();
1518
1519 crate::sql_query("INSERT INTO li_test (val) VALUES ('a')")
1520 .execute(conn)
1521 .unwrap();
1522 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
1523
1524 crate::sql_query("INSERT INTO li_test (val) VALUES ('b')")
1525 .execute(conn)
1526 .unwrap();
1527 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(2));
1528 }
1529
1530 #[cfg(not(miri))] #[diesel_test_helper::test]
1535 fn last_insert_rowid_unchanged_after_failed_insert() {
1536 let conn = &mut connection();
1537 crate::sql_query(
1538 "CREATE TABLE li_test2 (id INTEGER PRIMARY KEY, val TEXT NOT NULL UNIQUE)",
1539 )
1540 .execute(conn)
1541 .unwrap();
1542
1543 crate::sql_query("INSERT INTO li_test2 (val) VALUES ('a')")
1544 .execute(conn)
1545 .unwrap();
1546 let rowid = conn.last_insert_rowid();
1547 assert_eq!(rowid, NonZeroI64::new(1));
1548
1549 let result = crate::sql_query("INSERT INTO li_test2 (val) VALUES ('a')").execute(conn);
1551 assert!(result.is_err());
1552
1553 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
1555 }
1556
1557 #[diesel_test_helper::test]
1558 fn last_insert_rowid_with_explicit_rowid() {
1559 let conn = &mut connection();
1560 crate::sql_query("CREATE TABLE li_test3 (id INTEGER PRIMARY KEY, val TEXT NOT NULL)")
1561 .execute(conn)
1562 .unwrap();
1563
1564 crate::sql_query("INSERT INTO li_test3 (id, val) VALUES (42, 'a')")
1565 .execute(conn)
1566 .unwrap();
1567 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(42));
1568 }
1569
1570 #[diesel_test_helper::test]
1571 fn last_insert_rowid_unchanged_after_delete_and_update() {
1572 let conn = &mut connection();
1573 crate::sql_query("CREATE TABLE li_test4 (id INTEGER PRIMARY KEY, val TEXT NOT NULL)")
1574 .execute(conn)
1575 .unwrap();
1576
1577 crate::sql_query("INSERT INTO li_test4 (val) VALUES ('a')")
1578 .execute(conn)
1579 .unwrap();
1580 let rowid = conn.last_insert_rowid();
1581 assert_eq!(rowid, NonZeroI64::new(1));
1582
1583 crate::sql_query("UPDATE li_test4 SET val = 'b' WHERE id = 1")
1584 .execute(conn)
1585 .unwrap();
1586 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
1587
1588 crate::sql_query("DELETE FROM li_test4 WHERE id = 1")
1589 .execute(conn)
1590 .unwrap();
1591 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
1592 }
1593
1594 #[diesel_test_helper::test]
1595 fn test_injection() {
1596 diesel::table! {
1597 #[sql_name = "quote'table"]
1598 quote_table (id) {
1599 id -> Nullable<Integer>,
1600 name -> Nullable<Text>,
1601 }
1602 }
1603
1604 let mut conn = SqliteConnection::establish(":memory:").unwrap();
1605
1606 conn.batch_execute("CREATE TABLE \"quote'table\" (id INTEGER PRIMARY KEY, name TEXT);")
1607 .unwrap();
1608
1609 diesel::insert_into(quote_table::table)
1610 .values((quote_table::id.eq(1), quote_table::name.eq("Jane")))
1611 .execute(&mut conn)
1612 .unwrap();
1613
1614 if cfg!(not(miri)) {
1615 let data = quote_table::table
1617 .load::<(Option<i32>, Option<String>)>(&mut conn)
1618 .unwrap();
1619 assert_eq!(data, [(Some(1), Some("Jane".to_owned()))]);
1620 }
1621 }
1622
1623 #[crate::declare_sql_function]
1624 extern "SQL" {
1625 fn sqlite_version() -> Text;
1626 }
1627
1628 #[diesel_test_helper::test]
1629 #[cfg(not(miri))] fn version_matches_sqlite_version() {
1631 let conn = &mut connection();
1632 let reported = crate::select(sqlite_version())
1633 .get_result::<String>(conn)
1634 .unwrap();
1635 assert_eq!(conn.version().to_string(), reported);
1636 }
1637}