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;
10pub(super) mod borrowed_connection;
11mod collation_needed;
12mod db_config;
13mod functions;
14mod hooks;
15mod limits;
16#[cfg(all(
17 test,
18 feature = "std",
19 not(all(target_family = "wasm", target_os = "unknown")),
20 not(miri)
21))]
22#[allow(unsafe_code)]
23mod oom_test_support;
24mod owned_row;
25mod pragmas;
26mod raw;
27mod row;
28mod serialized_database;
29pub(in crate::sqlite) mod sqlite_blob;
30mod sqlite_value;
31mod statement_iterator;
32mod stmt;
33mod trace;
34mod update_hook;
35
36pub use self::authorizer::{AuthorizerContext, AuthorizerDecision};
37pub use self::bind_collector::SqliteBindCollector;#[diesel_derives::__diesel_public_if(
38 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
39)]
40pub(in crate::sqlite) use self::bind_collector::SqliteBindCollector;
41pub use self::bind_collector::SqliteBindValue;
42#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
43pub use self::bind_collector::{OwnedSqliteBindValue, SqliteBindCollectorData, SqliteBindValueRef};
44pub use self::collation_needed::{CollationNeededContext, SqliteTextRep};
45pub use self::limits::SqliteLimit;
46pub use self::pragmas::{AutoVacuumMode, WalCheckpointMode, WalCheckpointOutcome};
47use self::raw::RawConnection;
48pub use self::serialized_database::SerializedDatabase;
49pub use self::sqlite_value::SqliteValue;
50use self::statement_iterator::*;
51use self::stmt::{Statement, StatementUse};
52pub use self::trace::{SqliteTraceEvent, SqliteTraceFlags};
53pub use self::update_hook::{
54 SqliteChangeEvent, SqliteChangeOp, SqliteChangeOps, SqliteUpdateRouter,
55};
56use super::{BorrowedSqliteConnection, SqliteAggregateFunction};
57use crate::connection::instrumentation::{DynInstrumentation, StrQueryHelper};
58use crate::connection::statement_cache::StatementCache;
59use crate::connection::*;
60use crate::expression::QueryMetadata;
61use crate::query_builder::*;
62use crate::result::*;
63use crate::sql_types::TypeMetadata;
64use crate::sqlite::Sqlite;
65#[cfg(not(miri))]
66use alloc::string::String;
67use core::ffi as libc;
68use core::num::NonZeroI64;
69
70#[allow(missing_debug_implementations)]
202#[cfg(feature = "__sqlite-shared")]
203pub struct SqliteConnection {
204 statement_cache: StatementCache<Sqlite, Statement>,
208 raw_connection: RawConnection,
209 transaction_state: AnsiTransactionManager,
210 metadata_lookup: (),
213 instrumentation: DynInstrumentation,
214}
215
216#[allow(unsafe_code)]
220unsafe impl Send for SqliteConnection {}
221
222impl SimpleConnection for SqliteConnection {
223 fn batch_execute(&mut self, query: &str) -> QueryResult<()> {
224 self.instrumentation
225 .on_connection_event(InstrumentationEvent::StartQuery {
226 query: &StrQueryHelper::new(query),
227 });
228 let resp = self.raw_connection.exec(query);
229 self.instrumentation
230 .on_connection_event(InstrumentationEvent::FinishQuery {
231 query: &StrQueryHelper::new(query),
232 error: resp.as_ref().err(),
233 });
234 if resp.is_err() && self.raw_connection.is_autocommit() {
235 self.transaction_state.status = TransactionManagerStatus::Valid(Default::default());
237 }
238 resp
239 }
240}
241
242impl ConnectionSealed for SqliteConnection {}
243
244impl Connection for SqliteConnection {
245 type Backend = Sqlite;
246 type TransactionManager = AnsiTransactionManager;
247
248 fn establish(database_url: &str) -> ConnectionResult<Self> {
266 let mut instrumentation = DynInstrumentation::default_instrumentation();
267 instrumentation.on_connection_event(InstrumentationEvent::StartEstablishConnection {
268 url: database_url,
269 });
270
271 let establish_result = Self::establish_inner(database_url);
272 instrumentation.on_connection_event(InstrumentationEvent::FinishEstablishConnection {
273 url: database_url,
274 error: establish_result.as_ref().err(),
275 });
276 let mut conn = establish_result?;
277 conn.instrumentation = instrumentation;
278 Ok(conn)
279 }
280
281 fn execute_returning_count<T>(&mut self, source: &T) -> QueryResult<usize>
282 where
283 T: QueryFragment<Self::Backend> + QueryId,
284 {
285 let statement_use = self.prepared_query(source)?;
286 statement_use.run().and_then(|_| {
287 self.raw_connection
288 .rows_affected_by_last_query()
289 .map_err(Error::DeserializationError)
290 })
291 }
292
293 fn transaction_state(&mut self) -> &mut AnsiTransactionManager
294 where
295 Self: Sized,
296 {
297 &mut self.transaction_state
298 }
299
300 fn instrumentation(&mut self) -> &mut dyn Instrumentation {
301 &mut *self.instrumentation
302 }
303
304 fn set_instrumentation(&mut self, instrumentation: impl Instrumentation) {
305 self.instrumentation = instrumentation.into();
306 }
307
308 fn set_prepared_statement_cache_size(&mut self, size: CacheSize) {
309 self.statement_cache.set_cache_size(size);
310 }
311}
312
313impl LoadConnection<DefaultLoadingMode> for SqliteConnection {
314 type Cursor<'conn, 'query> = StatementIterator<'conn, 'query>;
315 type Row<'conn, 'query> = self::row::SqliteRow<'conn, 'query>;
316
317 fn load<'conn, 'query, T>(
318 &'conn mut self,
319 source: T,
320 ) -> QueryResult<Self::Cursor<'conn, 'query>>
321 where
322 T: Query + QueryFragment<Self::Backend> + QueryId + 'query,
323 Self::Backend: QueryMetadata<T::SqlType>,
324 {
325 let statement = self.prepared_query(source)?;
326
327 Ok(StatementIterator::new(statement))
328 }
329}
330
331impl WithMetadataLookup for SqliteConnection {
332 fn metadata_lookup(&mut self) -> &mut <Sqlite as TypeMetadata>::MetadataLookup {
333 &mut self.metadata_lookup
334 }
335}
336
337#[cfg(feature = "r2d2")]
338impl crate::r2d2::R2D2Connection for crate::sqlite::SqliteConnection {
339 fn ping(&mut self) -> QueryResult<()> {
340 use crate::RunQueryDsl;
341
342 crate::r2d2::CheckConnectionQuery.execute(self).map(|_| ())
343 }
344
345 fn is_broken(&mut self) -> bool {
346 AnsiTransactionManager::is_broken_transaction_manager(self)
347 }
348}
349
350impl MultiConnectionHelper for SqliteConnection {
351 fn to_any<'a>(
352 lookup: &mut <Self::Backend as crate::sql_types::TypeMetadata>::MetadataLookup,
353 ) -> &mut (dyn core::any::Any + 'a) {
354 lookup
355 }
356
357 fn from_any(
358 lookup: &mut dyn core::any::Any,
359 ) -> Option<&mut <Self::Backend as crate::sql_types::TypeMetadata>::MetadataLookup> {
360 lookup.downcast_mut()
361 }
362}
363
364#[derive(#[automatically_derived]impl ::core::fmt::Debug for CommitDecision {
#[inline]
#[rustc_trivial_field_reads]
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]
#[rustc_trivial_field_reads]
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)]
367pub enum CommitDecision {
368 Proceed,
370 Rollback,
372}
373
374#[derive(#[automatically_derived]impl ::core::fmt::Debug for ProgressDecision {
#[inline]
#[rustc_trivial_field_reads]
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]
#[rustc_trivial_field_reads]
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)]
377pub enum ProgressDecision {
378 Continue,
380 Interrupt,
382}
383
384#[derive(#[automatically_derived]impl ::core::fmt::Debug for BusyDecision {
#[inline]
#[rustc_trivial_field_reads]
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]
#[rustc_trivial_field_reads]
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)]
387pub enum BusyDecision {
388 Retry,
390 GiveUp,
392}
393
394impl SqliteConnection {
395 pub fn version(&self) -> DatabaseVersion {
409 let version = raw::library_version_number();
410 DatabaseVersion::new(
411 version / 1_000_000,
412 version / 1_000 % 1_000,
413 version % 1_000,
414 )
415 }
416
417 pub fn immediate_transaction<T, E, F>(&mut self, f: F) -> Result<T, E>
439 where
440 F: FnOnce(&mut Self) -> Result<T, E>,
441 E: From<Error>,
442 {
443 self.transaction_sql(f, "BEGIN IMMEDIATE")
444 }
445
446 pub fn exclusive_transaction<T, E, F>(&mut self, f: F) -> Result<T, E>
468 where
469 F: FnOnce(&mut Self) -> Result<T, E>,
470 E: From<Error>,
471 {
472 self.transaction_sql(f, "BEGIN EXCLUSIVE")
473 }
474
475 pub fn last_insert_rowid(&self) -> Option<NonZeroI64> {
511 NonZeroI64::new(self.raw_connection.last_insert_rowid())
512 }
513
514 fn transaction_sql<T, E, F>(&mut self, f: F, sql: &str) -> Result<T, E>
515 where
516 F: FnOnce(&mut Self) -> Result<T, E>,
517 E: From<Error>,
518 {
519 AnsiTransactionManager::begin_transaction_sql(&mut *self, sql)?;
520 match f(&mut *self) {
521 Ok(value) => {
522 AnsiTransactionManager::commit_transaction(&mut *self)?;
523 Ok(value)
524 }
525 Err(e) => {
526 AnsiTransactionManager::rollback_transaction(&mut *self)?;
527 Err(e)
528 }
529 }
530 }
531
532 fn prepared_query<'conn, 'query, T>(
533 &'conn mut self,
534 source: T,
535 ) -> QueryResult<StatementUse<'conn, 'query>>
536 where
537 T: QueryFragment<Sqlite> + QueryId + 'query,
538 {
539 self.instrumentation
540 .on_connection_event(InstrumentationEvent::StartQuery {
541 query: &crate::debug_query(&source),
542 });
543 let raw_connection = &self.raw_connection;
544 let cache = &mut self.statement_cache;
545 let statement = match cache.cached_statement(
546 &source,
547 &Sqlite,
548 &[],
549 raw_connection,
550 Statement::prepare,
551 &mut *self.instrumentation,
552 ) {
553 Ok(statement) => statement,
554 Err(e) => {
555 self.instrumentation
556 .on_connection_event(InstrumentationEvent::FinishQuery {
557 query: &crate::debug_query(&source),
558 error: Some(&e),
559 });
560
561 return Err(e);
562 }
563 };
564
565 StatementUse::bind(statement, source, &mut *self.instrumentation)
566 }
567
568 pub fn serialize_database_to_buffer(&mut self) -> SerializedDatabase {
579 self.raw_connection.serialize()
580 }
581
582 #[allow(unsafe_code)]
622 pub fn deserialize_readonly_database_from_buffer(&mut self, data: &[u8]) -> QueryResult<()> {
623 self.raw_connection.deserialize(data)
624 }
625
626 #[allow(unsafe_code)]
713 pub unsafe fn with_raw_connection<R, F>(&mut self, f: F) -> R
714 where
715 F: FnOnce(*mut ffi::sqlite3) -> R,
716 {
717 f(self.raw_connection.internal_connection.as_ptr())
718 }
719
720 #[allow(unsafe_code)]
729 pub(crate) unsafe fn with_borrowed_connection<R>(
730 db: core::ptr::NonNull<ffi::sqlite3>,
731 f: impl FnOnce(&'_ mut BorrowedSqliteConnection) -> R,
732 ) -> R {
733 let mut conn = BorrowedSqliteConnection::new(RawConnection::from_ptr(db));
734
735 let result = f(&mut conn);
736
737 unsafe {
738 if !#[allow(non_exhaustive_omitted_patterns)] match AnsiTransactionManager::transaction_manager_status_mut(conn.inner_mut()).transaction_depth()
{
Ok(None) => true,
_ => false,
} {
{
::core::panicking::panic_fmt(format_args!("callback must not leave an open transaction on the borrowed connection"));
}
};assert!(
744 matches!(
745 AnsiTransactionManager::transaction_manager_status_mut(conn.inner_mut())
746 .transaction_depth(),
747 Ok(None)
748 ),
749 "callback must not leave an open transaction on the borrowed connection"
750 );
751 }
752
753 result
754 }
755
756 fn register_diesel_sql_functions(&self) -> QueryResult<()> {
757 #[cfg(miri)]
765 return Ok(());
766
767 #[cfg(not(miri))]
768 {
769 use crate::sql_types::{Integer, Text};
770
771 self.raw_connection
775 .register_sql_function::<_, i32, Integer>(
776 "diesel_manage_updated_at",
777 1,
778 crate::sqlite::SqliteFunctionBehavior::DIRECTONLY,
779 |conn, args| {
780 let table_name: String = functions::build_sql_function_args::<Text, _>(
781 args,
782 conn.internal_connection,
783 )?;
784 let sql = ::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.replace('"', "\"\"")))
})alloc::format!(
787 include_str!("diesel_manage_updated_at.sql"),
788 table_name = table_name.replace('"', "\"\"")
789 );
790 Statement::prepare(
791 conn,
792 &sql,
793 crate::connection::statement_cache::PrepareForCache::No,
794 &[],
795 )?
796 .run()?;
797 Ok(0) },
799 )
800 }
801 }
802
803 fn establish_inner(database_url: &str) -> Result<SqliteConnection, ConnectionError> {
804 use crate::result::ConnectionError::CouldntSetupConfiguration;
805 let raw_connection = RawConnection::establish(database_url)?;
806 let conn = Self {
807 statement_cache: StatementCache::new(),
808 raw_connection,
809 transaction_state: AnsiTransactionManager::default(),
810 metadata_lookup: (),
811 instrumentation: DynInstrumentation::none(),
812 };
813 conn.register_diesel_sql_functions()
814 .map_err(CouldntSetupConfiguration)?;
815 let _ = conn
817 .raw_connection
818 .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, false);
819 Ok(conn)
820 }
821}
822
823fn error_message(err_code: libc::c_int) -> &'static str {
824 ffi::code_to_str(err_code)
825}
826
827#[cfg(all(test, not(miri)))]
829#[allow(unsafe_code)]
830pub(in crate::sqlite) fn leak_failed_cursor(conn: &mut SqliteConnection) {
831 use crate::connection::SimpleConnection;
832 use crate::query_builder::{AstPass, QueryFragment, QueryId};
833
834 struct OverflowQuery;
836
837 impl QueryId for OverflowQuery {
838 type QueryId = Self;
839 const HAS_STATIC_QUERY_ID: bool = true;
840 }
841
842 impl QueryFragment<Sqlite> for OverflowQuery {
843 fn walk_ast<'b>(&'b self, mut pass: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> {
844 pass.push_sql("SELECT abs(n) FROM overflow_numbers ORDER BY id");
845 Ok(())
846 }
847 }
848
849 const _: () = assert!(core::mem::size_of::<OverflowQuery>() == 0);
851
852 conn.batch_execute(
853 "CREATE TEMP TABLE IF NOT EXISTS overflow_numbers (id INTEGER PRIMARY KEY, n BIGINT);
854 INSERT OR IGNORE INTO overflow_numbers VALUES (1, 1), (2, -9223372036854775808);",
855 )
856 .unwrap();
857 let mut stmt = conn.prepared_query(OverflowQuery).unwrap();
858 let first = unsafe { stmt.step(true) };
860 assert!(matches!(first, Ok(true)));
861 let second = unsafe { stmt.step(true) };
864 assert!(second.is_err());
865 core::mem::forget(stmt);
868}
869
870#[cfg(test)]
871mod tests {
872 use super::*;
873 use crate::dsl::sql;
874 use crate::prelude::*;
875 use crate::sql_types::{Integer, Text};
876 #[cfg(not(miri))]
877 use crate::test_helpers::format_error;
878
879 fn connection() -> SqliteConnection {
880 SqliteConnection::establish(":memory:").unwrap()
881 }
882
883 #[diesel_test_helper::test]
884 #[allow(unsafe_code)]
885 fn with_raw_connection_can_return_values() {
886 let connection = &mut connection();
887
888 let autocommit_status = unsafe {
890 connection.with_raw_connection(|raw_conn| ffi::sqlite3_get_autocommit(raw_conn))
891 };
892
893 assert_ne!(autocommit_status, 0, "Expected autocommit to be enabled");
895 }
896
897 #[diesel_test_helper::test]
898 #[allow(unsafe_code)]
899 fn with_raw_connection_works_after_diesel_operations() {
900 let connection = &mut connection();
901
902 crate::sql_query("CREATE TABLE test_table (id INTEGER PRIMARY KEY, value TEXT)")
904 .execute(connection)
905 .unwrap();
906 crate::sql_query("INSERT INTO test_table (value) VALUES ('hello')")
907 .execute(connection)
908 .unwrap();
909
910 let last_rowid = unsafe {
912 connection.with_raw_connection(|raw_conn| ffi::sqlite3_last_insert_rowid(raw_conn))
913 };
914
915 assert_eq!(last_rowid, 1, "Last insert rowid should be 1");
916
917 let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM test_table")
919 .get_result(connection)
920 .unwrap();
921 assert_eq!(count, 1);
922 }
923
924 #[diesel_test_helper::test]
925 #[cfg(not(miri))] #[allow(unsafe_code)]
930 fn with_raw_connection_can_execute_raw_sql() {
931 let connection = &mut connection();
932
933 crate::sql_query("CREATE TABLE raw_test (id INTEGER PRIMARY KEY, name TEXT)")
935 .execute(connection)
936 .unwrap();
937
938 let result = unsafe {
941 connection.with_raw_connection(|raw_conn| {
942 let sql = c"INSERT INTO raw_test (name) VALUES ('from_raw')";
943 let mut err_msg: *mut libc::c_char = core::ptr::null_mut();
944 let rc = ffi::sqlite3_exec(
945 raw_conn,
946 sql.as_ptr(),
947 None,
948 core::ptr::null_mut(),
949 &mut err_msg,
950 );
951 if rc != ffi::SQLITE_OK && !err_msg.is_null() {
952 ffi::sqlite3_free(err_msg as *mut libc::c_void);
953 }
954 rc
955 })
956 };
957
958 assert_eq!(result, ffi::SQLITE_OK, "Raw SQL execution should succeed");
959
960 let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM raw_test")
962 .get_result(connection)
963 .unwrap();
964 assert_eq!(count, 1);
965
966 let name: String = sql::<Text>("SELECT name FROM raw_test WHERE id = 1")
967 .get_result(connection)
968 .unwrap();
969 assert_eq!(name, "from_raw");
970 }
971
972 #[diesel_test_helper::test]
973 #[allow(unsafe_code)]
974 fn with_raw_connection_works_within_transaction() {
975 let connection = &mut connection();
976
977 crate::sql_query("CREATE TABLE txn_test (id INTEGER PRIMARY KEY, value INTEGER)")
978 .execute(connection)
979 .unwrap();
980
981 connection
982 .transaction::<_, crate::result::Error, _>(|conn| {
983 crate::sql_query("INSERT INTO txn_test (value) VALUES (42)")
984 .execute(conn)
985 .unwrap();
986
987 let autocommit = unsafe {
989 conn.with_raw_connection(|raw_conn| ffi::sqlite3_get_autocommit(raw_conn))
990 };
991
992 assert_eq!(
994 autocommit, 0,
995 "Autocommit should be disabled inside transaction"
996 );
997
998 Ok(())
999 })
1000 .unwrap();
1001
1002 let autocommit = unsafe {
1004 connection.with_raw_connection(|raw_conn| ffi::sqlite3_get_autocommit(raw_conn))
1005 };
1006 assert_ne!(
1007 autocommit, 0,
1008 "Autocommit should be enabled after transaction"
1009 );
1010 }
1011
1012 #[diesel_test_helper::test]
1013 #[cfg(not(miri))] #[allow(unsafe_code)]
1018 fn with_raw_connection_can_read_database_filename() {
1019 let connection = &mut connection();
1020
1021 let filename = unsafe {
1023 connection.with_raw_connection(|raw_conn| {
1024 let db_name = c"main";
1025 let filename_ptr = ffi::sqlite3_db_filename(raw_conn, db_name.as_ptr());
1026 if filename_ptr.is_null() {
1027 None
1028 } else {
1029 let cstr = core::ffi::CStr::from_ptr(filename_ptr);
1031 Some(cstr.to_string_lossy().into_owned())
1032 }
1033 })
1034 };
1035
1036 assert_eq!(
1039 filename,
1040 Some(String::new()),
1041 "In-memory database filename should be an empty string"
1042 );
1043 }
1044
1045 #[diesel_test_helper::test]
1046 #[allow(unsafe_code)]
1047 fn with_raw_connection_changes_count() {
1048 let connection = &mut connection();
1049
1050 crate::sql_query("CREATE TABLE changes_test (id INTEGER PRIMARY KEY, value INTEGER)")
1051 .execute(connection)
1052 .unwrap();
1053
1054 crate::sql_query("INSERT INTO changes_test (value) VALUES (1), (2), (3)")
1055 .execute(connection)
1056 .unwrap();
1057
1058 let changes = unsafe {
1060 connection.with_raw_connection(|raw_conn| {
1061 let sql = c"UPDATE changes_test SET value = value + 10";
1062 let mut err_msg: *mut libc::c_char = core::ptr::null_mut();
1063 let rc = ffi::sqlite3_exec(
1064 raw_conn,
1065 sql.as_ptr(),
1066 None,
1067 core::ptr::null_mut(),
1068 &mut err_msg,
1069 );
1070 if rc != ffi::SQLITE_OK && !err_msg.is_null() {
1071 ffi::sqlite3_free(err_msg as *mut libc::c_void);
1072 return -1;
1073 }
1074 ffi::sqlite3_changes(raw_conn)
1075 })
1076 };
1077
1078 assert_eq!(changes, 3, "Should have updated 3 rows");
1079
1080 let values: Vec<i32> = sql::<Integer>("SELECT value FROM changes_test ORDER BY id")
1082 .load(connection)
1083 .unwrap();
1084 assert_eq!(values, vec![11, 12, 13]);
1085 }
1086
1087 #[diesel_test_helper::test]
1089 #[allow(unsafe_code)]
1090 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
1091 fn with_raw_connection_recovers_after_panic() {
1092 let connection = &mut connection();
1093
1094 crate::sql_query("CREATE TABLE panic_test (id INTEGER PRIMARY KEY, value TEXT)")
1095 .execute(connection)
1096 .unwrap();
1097
1098 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
1100 connection.with_raw_connection(|_raw_conn| {
1101 panic!("intentional panic inside with_raw_connection");
1102 })
1103 }));
1104 assert!(result.is_err(), "Should have caught the panic");
1105
1106 crate::sql_query("INSERT INTO panic_test (value) VALUES ('after_panic')")
1108 .execute(connection)
1109 .unwrap();
1110
1111 let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM panic_test")
1112 .get_result(connection)
1113 .unwrap();
1114 assert_eq!(count, 1, "Connection should work after panic in callback");
1115 }
1116
1117 #[diesel_test_helper::test]
1119 #[allow(unsafe_code)]
1120 #[cfg(not(miri))] #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
1125 fn with_raw_connection_can_read_file_database_filename() {
1126 let dir = std::env::temp_dir().join("diesel_test_filename.db");
1127 let db_path = dir.to_str().unwrap();
1128
1129 let _ = std::fs::remove_file(db_path);
1131
1132 let connection = &mut SqliteConnection::establish(db_path).unwrap();
1133
1134 let filename = unsafe {
1136 connection.with_raw_connection(|raw_conn| {
1137 let db_name = c"main";
1138 let filename_ptr = ffi::sqlite3_db_filename(raw_conn, db_name.as_ptr());
1139 if filename_ptr.is_null() {
1140 None
1141 } else {
1142 let cstr = core::ffi::CStr::from_ptr(filename_ptr);
1143 Some(cstr.to_string_lossy().into_owned())
1144 }
1145 })
1146 };
1147
1148 let filename = filename.expect("File-based database should have a filename");
1149 assert!(
1150 filename.contains("diesel_test_filename.db"),
1151 "Filename should contain the database name, got: {filename}"
1152 );
1153
1154 let _ = std::fs::remove_file(db_path);
1156 }
1157
1158 #[cfg(not(miri))] #[diesel_test_helper::test]
1163 fn database_serializes_and_deserializes_successfully() {
1164 let expected_users = vec![
1165 (
1166 1,
1167 "John Doe".to_string(),
1168 "john.doe@example.com".to_string(),
1169 ),
1170 (
1171 2,
1172 "Jane Doe".to_string(),
1173 "jane.doe@example.com".to_string(),
1174 ),
1175 ];
1176
1177 let conn1 = &mut connection();
1178 let _ =
1179 crate::sql_query("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)")
1180 .execute(conn1);
1181 let _ = crate::sql_query("INSERT INTO users (name, email) VALUES ('John Doe', 'john.doe@example.com'), ('Jane Doe', 'jane.doe@example.com')")
1182 .execute(conn1);
1183
1184 for _i in 0..2 {
1185 let serialized_database = conn1.serialize_database_to_buffer();
1186 let conn2 = &mut connection();
1187 conn2
1188 .deserialize_readonly_database_from_buffer(
1189 serialized_database.try_as_slice().unwrap(),
1190 )
1191 .unwrap();
1192
1193 let query =
1194 sql::<(Integer, Text, Text)>("SELECT id, name, email FROM users ORDER BY id");
1195 let actual_users = query.load::<(i32, String, String)>(conn2).unwrap();
1196
1197 assert_eq!(expected_users, actual_users);
1198 std::mem::drop(serialized_database);
1202 let query =
1203 sql::<(Integer, Text, Text)>("SELECT id, name, email FROM users ORDER BY id");
1204 let actual_users = query.load::<(i32, String, String)>(conn2).unwrap();
1205
1206 assert_eq!(expected_users, actual_users);
1207 }
1208 }
1209
1210 #[cfg(not(miri))] #[diesel_test_helper::test]
1215 fn database_deserialize_random_bytes() {
1216 let buffer = vec![0, 1, 2, 3, 4];
1217 let conn = &mut SqliteConnection::establish(":memory:").unwrap();
1218
1219 conn.deserialize_readonly_database_from_buffer(&buffer)
1220 .unwrap();
1221
1222 let r = sql::<Integer>("SELECT id FROM users").load::<i32>(conn);
1223
1224 assert!(r.is_err());
1225 assert_eq!(format_error(&r.unwrap_err()), "file is not a database");
1226
1227 let conn = &mut SqliteConnection::establish(":memory:").unwrap();
1228
1229 let _ =
1230 crate::sql_query("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)")
1231 .execute(conn);
1232 let _ = crate::sql_query("INSERT INTO users (name, email) VALUES ('John Doe', 'john.doe@example.com'), ('Jane Doe', 'jane.doe@example.com')")
1233 .execute(conn);
1234
1235 let db = conn.serialize_database_to_buffer();
1236 let mut bad_buffer = db[..100].to_vec();
1238 bad_buffer.extend(b"whatever");
1239 conn.deserialize_readonly_database_from_buffer(&bad_buffer)
1240 .unwrap();
1241
1242 let r = sql::<Integer>("SELECT id FROM users").load::<i32>(conn);
1243
1244 assert!(r.is_err());
1245 assert_eq!(
1246 format_error(&r.unwrap_err()),
1247 "database disk image is malformed"
1248 );
1249
1250 let mut size_fitting_bad_buffer = db[..100].to_vec();
1252 size_fitting_bad_buffer.extend(
1253 core::iter::repeat(b"abcdefghij")
1254 .flatten()
1255 .take(db.len() - 100),
1256 );
1257 let r = conn.deserialize_readonly_database_from_buffer(&size_fitting_bad_buffer);
1258
1259 assert!(r.is_err());
1260 assert_eq!(
1261 format_error(&r.unwrap_err()),
1262 "database disk image is malformed"
1263 );
1264 }
1265
1266 #[diesel_test_helper::test]
1267 fn database_serializes_empty_deserialized_database() {
1268 let conn = &mut SqliteConnection::establish(":memory:").unwrap();
1269 conn.deserialize_readonly_database_from_buffer(&[]).unwrap();
1270
1271 let serialized = conn.serialize_database_to_buffer();
1272
1273 assert!(serialized.is_empty());
1274 assert!(serialized.try_as_slice().unwrap().is_empty());
1275 }
1276
1277 #[cfg(all(
1278 feature = "std",
1279 not(all(target_family = "wasm", target_os = "unknown")),
1280 not(miri)
1284 ))]
1285 #[allow(unsafe_code)]
1286 mod sqlite_serialize_oom {
1287 use super::super::oom_test_support::{panic_message, run_in_child, with_heap_limit};
1288 use super::super::{SerializedDatabase, ffi};
1289 use crate::connection::{Connection, SimpleConnection};
1290 use crate::sqlite::SqliteConnection;
1291 use crate::test_helpers::format_error;
1292
1293 const MIN_DATABASE_BYTES: i64 = 1_048_576;
1294
1295 fn with_failing_serialize<R>(f: impl FnOnce() -> R) -> R {
1298 with_heap_limit(65_536, f)
1299 }
1300
1301 #[test]
1302 fn sqlite_serialize_oom_is_contained() {
1303 run_in_child(|| {
1304 let mut conn = large_database();
1305
1306 let (baseline_size, baseline) = serialize_direct(&conn);
1307 assert!(
1308 baseline_size >= MIN_DATABASE_BYTES,
1309 "the serialized database is smaller than 1 MiB"
1310 );
1311 assert!(
1312 !baseline.is_null(),
1313 "SQLite refused to serialize a valid database"
1314 );
1315 unsafe { ffi::sqlite3_free(baseline as _) };
1317
1318 let (reported_size, data) = with_failing_serialize(|| serialize_direct(&conn));
1319 if !data.is_null() {
1320 unsafe { ffi::sqlite3_free(data as _) };
1322 }
1323 assert!(
1324 data.is_null(),
1325 "SQLite did not fail the output allocation of the serialization"
1326 );
1327 assert!(
1329 reported_size >= MIN_DATABASE_BYTES,
1330 "SQLite reported a serialization size of {reported_size} with a null buffer"
1331 );
1332
1333 let serialized: SerializedDatabase =
1334 with_failing_serialize(|| conn.serialize_database_to_buffer());
1335 let error = serialized
1336 .try_as_slice()
1337 .expect_err("the failed output allocation must surface as an error");
1338 assert_eq!(format_error(&error), "out of memory");
1339
1340 let payload = std::panic::catch_unwind(core::panic::AssertUnwindSafe(|| {
1341 core::hint::black_box(serialized[0]);
1342 }))
1343 .expect_err("the serialized database access did not panic");
1344 let message = panic_message(&*payload);
1345 assert!(
1346 message.contains("Cannot access the serialized database: out of memory"),
1347 "SQLite serialization allocation failure surfaced as `{message}` instead \
1348 of a caught allocation panic"
1349 );
1350 });
1351 }
1352
1353 fn large_database() -> SqliteConnection {
1354 let mut conn = SqliteConnection::establish(":memory:").unwrap();
1355 conn.batch_execute(&format!(
1356 "CREATE TABLE blobs (id INTEGER PRIMARY KEY, payload BLOB);
1357 INSERT INTO blobs (payload) VALUES (zeroblob({MIN_DATABASE_BYTES}));"
1358 ))
1359 .unwrap();
1360 conn
1361 }
1362
1363 fn serialize_direct(conn: &SqliteConnection) -> (ffi::sqlite3_int64, *mut u8) {
1364 unsafe {
1366 let mut size: ffi::sqlite3_int64 = 0;
1367 let data = ffi::sqlite3_serialize(
1368 conn.raw_connection.internal_connection.as_ptr(),
1369 core::ptr::null(),
1370 &mut size as *mut _,
1371 0,
1372 );
1373 (size, data)
1374 }
1375 }
1376 }
1377
1378 #[cfg(all(
1379 feature = "std",
1380 not(all(target_family = "wasm", target_os = "unknown")),
1381 not(miri)
1382 ))]
1383 mod diesel_manage_updated_at_errors {
1384 use super::super::oom_test_support::run_in_child;
1385 use crate::connection::{Connection, SimpleConnection};
1386 use crate::query_dsl::RunQueryDsl;
1387 use crate::sqlite::{AuthorizerContext, AuthorizerDecision, SqliteConnection};
1388 use crate::test_helpers::format_error;
1389
1390 #[track_caller]
1391 fn assert_no_panic_output(stderr: &str, table_name: &str) {
1392 assert!(
1393 !stderr.contains("panicked") && !stderr.contains(table_name),
1394 "diesel_manage_updated_at reached the panic hook\nstderr:\n{stderr}"
1395 );
1396 }
1397
1398 #[test]
1399 fn missing_table_returns_error_without_panicking() {
1400 let stderr = run_in_child(|| {
1401 let mut conn = SqliteConnection::establish(":memory:").unwrap();
1402 let error = conn
1403 .batch_execute("SELECT diesel_manage_updated_at('synthetic_key_7c91a2')")
1404 .unwrap_err();
1405 assert_eq!(
1406 format_error(&error),
1407 "no such table: main.synthetic_key_7c91a2"
1408 );
1409 });
1410 assert_no_panic_output(&stderr, "synthetic_key_7c91a2");
1411 }
1412
1413 #[test]
1414 fn denied_trigger_returns_error_without_panicking() {
1415 let stderr = run_in_child(|| {
1416 let mut conn = SqliteConnection::establish(":memory:").unwrap();
1417 conn.batch_execute("CREATE TABLE guarded_7c91a2 (id INTEGER, updated_at TEXT)")
1418 .unwrap();
1419 conn.on_authorize(|ctx| match ctx {
1420 AuthorizerContext::CreateTrigger(_) => AuthorizerDecision::Deny,
1421 _ => AuthorizerDecision::Allow,
1422 });
1423 let error = conn
1424 .batch_execute("SELECT diesel_manage_updated_at('guarded_7c91a2')")
1425 .unwrap_err();
1426 assert_eq!(format_error(&error), "not authorized");
1427
1428 conn.remove_authorizer();
1429 let triggers: i64 = crate::dsl::sql::<crate::sql_types::BigInt>(
1430 "SELECT COUNT(*) FROM sqlite_master WHERE type = 'trigger'",
1431 )
1432 .get_result(&mut conn)
1433 .unwrap();
1434 assert_eq!(triggers, 0);
1435 });
1436 assert_no_panic_output(&stderr, "guarded_7c91a2");
1437 }
1438
1439 #[test]
1440 fn failed_step_returns_error_without_panicking() {
1441 let stderr = run_in_child(|| {
1442 let mut conn = SqliteConnection::establish(":memory:").unwrap();
1443 conn.batch_execute(
1444 "CREATE TABLE readonly_7c91a2 (id INTEGER, updated_at TEXT);
1445 PRAGMA query_only = ON;",
1446 )
1447 .unwrap();
1448 let error = conn
1449 .batch_execute("SELECT diesel_manage_updated_at('readonly_7c91a2')")
1450 .unwrap_err();
1451 assert_eq!(format_error(&error), "attempt to write a readonly database");
1452 });
1453 assert_no_panic_output(&stderr, "readonly_7c91a2");
1454 }
1455 }
1456
1457 #[cfg(not(miri))]
1458 #[diesel_test_helper::test]
1459 fn diesel_manage_updated_at_does_not_execute_its_argument() {
1460 let connection = &mut SqliteConnection::establish(":memory:").unwrap();
1461 connection
1462 .batch_execute(
1463 "CREATE TABLE t (id INTEGER PRIMARY KEY, updated_at TEXT);
1464 CREATE TABLE victims (id INTEGER PRIMARY KEY);",
1465 )
1466 .unwrap();
1467 let payload = "t AFTER UPDATE ON t BEGIN SELECT 1; END; DROP TABLE victims; --";
1468
1469 let result = sql::<Integer>("SELECT diesel_manage_updated_at(")
1470 .bind::<Text, _>(payload)
1471 .sql(")")
1472 .get_result::<i32>(connection);
1473
1474 let schema = sql::<Text>("SELECT group_concat(name, ',') FROM sqlite_master")
1475 .get_result::<String>(connection)
1476 .unwrap();
1477 assert_eq!(schema, "t,victims");
1478 assert_eq!(
1479 result.map_err(|e| format_error(&e)),
1480 Err(format!("no such table: main.{payload}"))
1481 );
1482 }
1483
1484 #[cfg(not(miri))]
1485 #[diesel_test_helper::test]
1486 fn diesel_manage_updated_at_quotes_the_table_name() {
1487 let connection = &mut SqliteConnection::establish(":memory:").unwrap();
1488 let names = ["users", "select", "odd \"name\"", "a b", "a_b"];
1489 for name in names {
1490 let quoted = name.replace('"', "\"\"");
1491 connection
1492 .batch_execute(&format!(
1493 "CREATE TABLE \"{quoted}\" (id INTEGER PRIMARY KEY, updated_at TEXT);
1494 INSERT INTO \"{quoted}\" (id) VALUES (1);"
1495 ))
1496 .unwrap();
1497 sql::<Integer>("SELECT diesel_manage_updated_at(")
1498 .bind::<Text, _>(name)
1499 .sql(")")
1500 .get_result::<i32>(connection)
1501 .unwrap();
1502 connection
1503 .batch_execute(&format!("UPDATE \"{quoted}\" SET id = 2"))
1504 .unwrap();
1505 let updated = sql::<Integer>(&format!(
1506 "SELECT COUNT(*) FROM \"{quoted}\" WHERE updated_at IS NOT NULL"
1507 ))
1508 .get_result::<i32>(connection)
1509 .unwrap();
1510 assert_eq!(updated, 1, "table {name}");
1511 }
1512
1513 let triggers =
1514 sql::<Text>("SELECT name FROM sqlite_master WHERE type = 'trigger' ORDER BY rowid")
1515 .load::<String>(connection)
1516 .unwrap();
1517 let expected = names.map(|name| format!("__diesel_manage_updated_at_{name}"));
1518 assert_eq!(triggers, expected);
1519 }
1520
1521 #[diesel_test_helper::test]
1523 fn test_correct_serialization_of_owned_strings() {
1524 use crate::prelude::*;
1525
1526 #[derive(Debug, crate::expression::AsExpression)]
1527 #[diesel(sql_type = diesel::sql_types::Text)]
1528 struct CustomWrapper(String);
1529
1530 impl crate::serialize::ToSql<Text, Sqlite> for CustomWrapper {
1531 fn to_sql<'b>(
1532 &'b self,
1533 out: &mut crate::serialize::Output<'b, '_, Sqlite>,
1534 ) -> crate::serialize::Result {
1535 out.set_value(self.0.to_string());
1536 Ok(crate::serialize::IsNull::No)
1537 }
1538 }
1539
1540 let connection = &mut connection();
1541
1542 let res = crate::select(
1543 CustomWrapper("".into())
1544 .into_sql::<crate::sql_types::Text>()
1545 .nullable(),
1546 )
1547 .get_result::<Option<String>>(connection)
1548 .unwrap();
1549 assert_eq!(res, Some(String::new()));
1550 }
1551
1552 #[diesel_test_helper::test]
1553 fn test_correct_serialization_of_owned_bytes() {
1554 use crate::prelude::*;
1555
1556 #[derive(Debug, crate::expression::AsExpression)]
1557 #[diesel(sql_type = diesel::sql_types::Binary)]
1558 struct CustomWrapper(Vec<u8>);
1559
1560 impl crate::serialize::ToSql<crate::sql_types::Binary, Sqlite> for CustomWrapper {
1561 fn to_sql<'b>(
1562 &'b self,
1563 out: &mut crate::serialize::Output<'b, '_, Sqlite>,
1564 ) -> crate::serialize::Result {
1565 out.set_value(self.0.clone());
1566 Ok(crate::serialize::IsNull::No)
1567 }
1568 }
1569
1570 let connection = &mut connection();
1571
1572 let res = crate::select(
1573 CustomWrapper(Vec::new())
1574 .into_sql::<crate::sql_types::Binary>()
1575 .nullable(),
1576 )
1577 .get_result::<Option<Vec<u8>>>(connection)
1578 .unwrap();
1579 assert_eq!(res, Some(Vec::new()));
1580 }
1581
1582 #[diesel_test_helper::test]
1583 fn correctly_handle_empty_query() {
1584 let check_empty_query_error = |r: crate::QueryResult<usize>| {
1585 assert!(r.is_err());
1586 let err = r.unwrap_err();
1587 assert!(
1588 matches!(err, crate::result::Error::QueryBuilderError(ref b) if b.is::<crate::result::EmptyQuery>()),
1589 "Expected a query builder error, but got {err}"
1590 );
1591 };
1592 let connection = &mut SqliteConnection::establish(":memory:").unwrap();
1593 check_empty_query_error(crate::sql_query("").execute(connection));
1594 check_empty_query_error(crate::sql_query(" ").execute(connection));
1595 check_empty_query_error(crate::sql_query("\n\t").execute(connection));
1596 check_empty_query_error(crate::sql_query("-- SELECT 1;").execute(connection));
1597 }
1598
1599 #[diesel_test_helper::test]
1600 fn last_insert_rowid_returns_none_on_fresh_connection() {
1601 let conn = &mut connection();
1602 assert_eq!(conn.last_insert_rowid(), None);
1603 }
1604
1605 #[diesel_test_helper::test]
1606 fn last_insert_rowid_returns_rowid_after_insert() {
1607 let conn = &mut connection();
1608 crate::sql_query("CREATE TABLE li_test (id INTEGER PRIMARY KEY, val TEXT NOT NULL)")
1609 .execute(conn)
1610 .unwrap();
1611
1612 crate::sql_query("INSERT INTO li_test (val) VALUES ('a')")
1613 .execute(conn)
1614 .unwrap();
1615 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
1616
1617 crate::sql_query("INSERT INTO li_test (val) VALUES ('b')")
1618 .execute(conn)
1619 .unwrap();
1620 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(2));
1621 }
1622
1623 #[cfg(not(miri))] #[diesel_test_helper::test]
1628 fn last_insert_rowid_unchanged_after_failed_insert() {
1629 let conn = &mut connection();
1630 crate::sql_query(
1631 "CREATE TABLE li_test2 (id INTEGER PRIMARY KEY, val TEXT NOT NULL UNIQUE)",
1632 )
1633 .execute(conn)
1634 .unwrap();
1635
1636 crate::sql_query("INSERT INTO li_test2 (val) VALUES ('a')")
1637 .execute(conn)
1638 .unwrap();
1639 let rowid = conn.last_insert_rowid();
1640 assert_eq!(rowid, NonZeroI64::new(1));
1641
1642 let result = crate::sql_query("INSERT INTO li_test2 (val) VALUES ('a')").execute(conn);
1644 assert!(result.is_err());
1645
1646 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
1648 }
1649
1650 #[diesel_test_helper::test]
1651 fn last_insert_rowid_with_explicit_rowid() {
1652 let conn = &mut connection();
1653 crate::sql_query("CREATE TABLE li_test3 (id INTEGER PRIMARY KEY, val TEXT NOT NULL)")
1654 .execute(conn)
1655 .unwrap();
1656
1657 crate::sql_query("INSERT INTO li_test3 (id, val) VALUES (42, 'a')")
1658 .execute(conn)
1659 .unwrap();
1660 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(42));
1661 }
1662
1663 #[diesel_test_helper::test]
1664 fn last_insert_rowid_unchanged_after_delete_and_update() {
1665 let conn = &mut connection();
1666 crate::sql_query("CREATE TABLE li_test4 (id INTEGER PRIMARY KEY, val TEXT NOT NULL)")
1667 .execute(conn)
1668 .unwrap();
1669
1670 crate::sql_query("INSERT INTO li_test4 (val) VALUES ('a')")
1671 .execute(conn)
1672 .unwrap();
1673 let rowid = conn.last_insert_rowid();
1674 assert_eq!(rowid, NonZeroI64::new(1));
1675
1676 crate::sql_query("UPDATE li_test4 SET val = 'b' WHERE id = 1")
1677 .execute(conn)
1678 .unwrap();
1679 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
1680
1681 crate::sql_query("DELETE FROM li_test4 WHERE id = 1")
1682 .execute(conn)
1683 .unwrap();
1684 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
1685 }
1686
1687 #[diesel_test_helper::test]
1688 fn test_injection() {
1689 diesel::table! {
1690 #[sql_name = "quote'table"]
1691 quote_table (id) {
1692 id -> Nullable<Integer>,
1693 name -> Nullable<Text>,
1694 }
1695 }
1696
1697 let mut conn = SqliteConnection::establish(":memory:").unwrap();
1698
1699 conn.batch_execute("CREATE TABLE \"quote'table\" (id INTEGER PRIMARY KEY, name TEXT);")
1700 .unwrap();
1701
1702 diesel::insert_into(quote_table::table)
1703 .values((quote_table::id.eq(1), quote_table::name.eq("Jane")))
1704 .execute(&mut conn)
1705 .unwrap();
1706
1707 if cfg!(not(miri)) {
1708 let data = quote_table::table
1710 .load::<(Option<i32>, Option<String>)>(&mut conn)
1711 .unwrap();
1712 assert_eq!(data, [(Some(1), Some("Jane".to_owned()))]);
1713 }
1714 }
1715
1716 #[crate::declare_sql_function]
1717 extern "SQL" {
1718 fn sqlite_version() -> Text;
1719 }
1720
1721 #[diesel_test_helper::test]
1722 #[cfg(not(miri))] fn version_matches_sqlite_version() {
1724 let conn = &mut connection();
1725 let reported = crate::select(sqlite_version())
1726 .get_result::<String>(conn)
1727 .unwrap();
1728 assert_eq!(conn.version().to_string(), reported);
1729 }
1730
1731 #[cfg(all(
1732 feature = "std",
1733 not(all(target_family = "wasm", target_os = "unknown")),
1734 not(miri)
1736 ))]
1737 #[diesel_test_helper::test]
1738 fn deserialize_in_auto_extension_keeps_buffer_alive() {
1739 use super::oom_test_support::run_in_child;
1740
1741 run_in_child(|| {
1742 use std::sync::OnceLock;
1743 #[allow(unsafe_code, reason = "Test code")]
1744 fn load_image(conn: &mut BorrowedSqliteConnection) -> QueryResult<()> {
1745 unsafe {
1748 conn.inner_mut()
1749 .deserialize_readonly_database_from_buffer(IMAGE.get().unwrap())
1750 }
1751 }
1752
1753 static IMAGE: OnceLock<Vec<u8>> = OnceLock::new();
1754
1755 let mut src = SqliteConnection::establish(":memory:").unwrap();
1756 src.batch_execute("CREATE TABLE t(x TEXT); INSERT INTO t VALUES ('hello');")
1757 .unwrap();
1758 let image = src
1759 .serialize_database_to_buffer()
1760 .try_as_slice()
1761 .unwrap()
1762 .to_vec();
1763 IMAGE.set(image).unwrap();
1764
1765 crate::sqlite::register_auto_extension(load_image).unwrap();
1766 let mut conn = SqliteConnection::establish(":memory:").unwrap();
1767 crate::sqlite::reset_auto_extension();
1768
1769 let rows: QueryResult<Vec<String>> = sql::<Text>("SELECT x FROM t").load(&mut conn);
1770 assert_eq!(rows.unwrap(), vec!["hello".to_string()]);
1771 });
1772 }
1773}