1#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
2extern crate libsqlite3_sys as ffi;
34#[cfg(all(target_family = "wasm", target_os = "unknown"))]
5use sqlite_wasm_rs as ffi;
67mod 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;
3435pub 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::{
53SqliteChangeEvent, 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::ffias libc;
67use core::num::NonZeroI64;
6869/// Connections for the SQLite backend. Unlike other backends, SQLite supported
70/// connection URLs are:
71///
72/// - File paths (`test.db`)
73/// - [URIs](https://sqlite.org/uri.html) (`file://test.db`)
74/// - Special identifiers (`:memory:`)
75///
76/// # Supported loading model implementations
77///
78/// * [`DefaultLoadingMode`]
79///
80/// As `SqliteConnection` only supports a single loading mode implementation,
81/// it is **not required** to explicitly specify a loading mode
82/// when calling [`RunQueryDsl::load_iter()`] or [`LoadConnection::load`]
83///
84/// [`RunQueryDsl::load_iter()`]: crate::query_dsl::RunQueryDsl::load_iter
85///
86/// ## DefaultLoadingMode
87///
88/// `SqliteConnection` only supports a single loading mode, which loads
89/// values row by row from the result set.
90///
91/// ```rust,dejadoc
92/// # include!("../../doctest_setup.rs");
93/// #
94/// # fn main() {
95/// # run_test().unwrap();
96/// # }
97/// #
98/// # fn run_test() -> QueryResult<()> {
99/// # use schema::users;
100/// # let connection = &mut establish_connection();
101/// use diesel::connection::DefaultLoadingMode;
102/// {
103/// // scope to restrict the lifetime of the iterator
104/// let iter1 = users::table.load_iter::<(i32, String), DefaultLoadingMode>(connection)?;
105///
106/// for r in iter1 {
107/// let (id, name) = r?;
108/// println!("Id: {} Name: {}", id, name);
109/// }
110/// }
111///
112/// // works without specifying the loading mode
113/// let iter2 = users::table.load_iter::<(i32, String), _>(connection)?;
114///
115/// for r in iter2 {
116/// let (id, name) = r?;
117/// println!("Id: {} Name: {}", id, name);
118/// }
119/// # Ok(())
120/// # }
121/// ```
122///
123/// This mode does **not support** creating
124/// multiple iterators using the same connection.
125///
126/// ```compile_fail,dejadoc
127/// # include!("../../doctest_setup.rs");
128/// #
129/// # fn main() {
130/// # run_test().unwrap();
131/// # }
132/// #
133/// # fn run_test() -> QueryResult<()> {
134/// # use schema::users;
135/// # let connection = &mut establish_connection();
136/// use diesel::connection::DefaultLoadingMode;
137///
138/// let iter1 = users::table.load_iter::<(i32, String), DefaultLoadingMode>(connection)?;
139/// let iter2 = users::table.load_iter::<(i32, String), DefaultLoadingMode>(connection)?;
140///
141/// for r in iter1 {
142/// let (id, name) = r?;
143/// println!("Id: {} Name: {}", id, name);
144/// }
145///
146/// for r in iter2 {
147/// let (id, name) = r?;
148/// println!("Id: {} Name: {}", id, name);
149/// }
150/// # Ok(())
151/// # }
152/// ```
153///
154/// # Concurrency
155///
156/// By default, when running into a database lock, the operation will abort with a
157/// `Database locked` error. However, it's possible to configure it for greater concurrency,
158/// trading latency for not having to deal with retries yourself.
159///
160/// You can use this example as blue-print for which statements to run after establishing a connection.
161/// It is **important** to run each `PRAGMA` in a single statement to make sure all of them apply
162/// correctly. In addition the order of the `PRAGMA` statements is relevant to prevent timeout
163/// issues for the later `PRAGMA` statements.
164///
165/// ```rust
166/// # include!("../../doctest_setup.rs");
167/// #
168/// # fn main() {
169/// # run_test().unwrap();
170/// # }
171/// #
172/// # fn run_test() -> QueryResult<()> {
173/// # use schema::users;
174/// use diesel::connection::SimpleConnection;
175/// use diesel::sqlite::WalCheckpointMode;
176/// let conn = &mut establish_connection();
177/// // see https://fractaledmind.github.io/2023/09/07/enhancing-rails-sqlite-fine-tuning/
178/// // sleep if the database is busy, this corresponds to up to 2 seconds sleeping time.
179/// conn.batch_execute("PRAGMA busy_timeout = 2000;")?;
180/// // better write-concurrency
181/// conn.batch_execute("PRAGMA journal_mode = WAL;")?;
182/// // fsync only in critical moments
183/// conn.batch_execute("PRAGMA synchronous = NORMAL;")?;
184/// // write WAL changes back every 1000 pages, for an in average 1MB WAL file.
185/// // May affect readers if number is increased
186/// conn.batch_execute("PRAGMA wal_autocheckpoint = 1000;")?;
187/// // free some space by truncating possibly massive WAL files from the last run
188/// conn.wal_checkpoint(None, WalCheckpointMode::Truncate)?;
189/// # Ok(())
190/// # }
191/// ```
192#[allow(missing_debug_implementations)]
193#[cfg(feature = "__sqlite-shared")]
194pub struct SqliteConnection {
195// statement_cache needs to be before raw_connection
196 // otherwise we will get errors about open statements before closing the
197 // connection itself
198statement_cache: StatementCache<Sqlite, Statement>,
199 raw_connection: RawConnection,
200 transaction_state: AnsiTransactionManager,
201// this exists for the sole purpose of implementing `WithMetadataLookup` trait
202 // and avoiding static mut which will be deprecated in 2024 edition
203metadata_lookup: (),
204 instrumentation: DynInstrumentation,
205// We potentially need to store a serialized
206 // database in here to make sure the database bytes
207 // live as long as the connection
208 // This is used by SqliteConnection::deserialize_readonly_database_from_buffer
209 // only
210 // This field needs to come after the RawConnection
211 // as we need to make sure the data are still there until the
212 // connection is dropped
213 //
214 // We are not allowed to modify the inner buffer until the database connection is dropped
215serialized_data: Vec<Vec<u8>>,
216}
217218// This relies on the invariant that RawConnection or Statement are never
219// leaked. If a reference to one of those was held on a different thread, this
220// would not be thread safe.
221#[allow(unsafe_code)]
222unsafe impl Sendfor SqliteConnection {}
223224impl SimpleConnectionfor SqliteConnection {
225fn batch_execute(&mut self, query: &str) -> QueryResult<()> {
226self.instrumentation
227 .on_connection_event(InstrumentationEvent::StartQuery {
228 query: &StrQueryHelper::new(query),
229 });
230let resp = self.raw_connection.exec(query);
231self.instrumentation
232 .on_connection_event(InstrumentationEvent::FinishQuery {
233 query: &StrQueryHelper::new(query),
234 error: resp.as_ref().err(),
235 });
236if resp.is_err() && self.raw_connection.is_autocommit() {
237// SQLite ends the transaction on some failures, e.g. an aborting commit hook.
238self.transaction_state.status = TransactionManagerStatus::Valid(Default::default());
239 }
240resp241 }
242}
243244impl ConnectionSealedfor SqliteConnection {}
245246impl Connectionfor SqliteConnection {
247type Backend = Sqlite;
248type TransactionManager = AnsiTransactionManager;
249250/// Establish a connection to the database specified by `database_url`.
251 ///
252 /// See [SqliteConnection] for supported `database_url`.
253 ///
254 /// If the database does not exist, this method will try to
255 /// create a new database and then establish a connection to it.
256 ///
257 /// ## WASM support
258 ///
259 /// If you plan to use this connection type on the `wasm32-unknown-unknown` target please
260 /// make sure to read the following notes:
261 ///
262 /// * The database is stored in memory by default.
263 /// * With `sqlite-wasm-rs` 0.6, enable its `wasm-bindgen` feature to use the
264 /// built-in host functions, or provide your own.
265 /// * Persistent VFS (Virtual File Systems) is optional,
266 /// see <https://github.com/Spxg/sqlite-wasm-rs> for details
267fn establish(database_url: &str) -> ConnectionResult<Self> {
268let mut instrumentation = DynInstrumentation::default_instrumentation();
269instrumentation.on_connection_event(InstrumentationEvent::StartEstablishConnection {
270 url: database_url,
271 });
272273let establish_result = Self::establish_inner(database_url);
274instrumentation.on_connection_event(InstrumentationEvent::FinishEstablishConnection {
275 url: database_url,
276 error: establish_result.as_ref().err(),
277 });
278let mut conn = establish_result?;
279conn.instrumentation = instrumentation;
280Ok(conn)
281 }
282283fn execute_returning_count<T>(&mut self, source: &T) -> QueryResult<usize>
284where
285T: QueryFragment<Self::Backend> + QueryId,
286 {
287let statement_use = self.prepared_query(source)?;
288statement_use.run().and_then(|_| {
289self.raw_connection
290 .rows_affected_by_last_query()
291 .map_err(Error::DeserializationError)
292 })
293 }
294295fn transaction_state(&mut self) -> &mut AnsiTransactionManager296where
297Self: Sized,
298 {
299&mut self.transaction_state
300 }
301302fn instrumentation(&mut self) -> &mut dyn Instrumentation {
303&mut *self.instrumentation
304 }
305306fn set_instrumentation(&mut self, instrumentation: impl Instrumentation) {
307self.instrumentation = instrumentation.into();
308 }
309310fn set_prepared_statement_cache_size(&mut self, size: CacheSize) {
311self.statement_cache.set_cache_size(size);
312 }
313}
314315impl LoadConnection<DefaultLoadingMode> for SqliteConnection {
316type Cursor<'conn, 'query> = StatementIterator<'conn, 'query>;
317type Row<'conn, 'query> = self::row::SqliteRow<'conn, 'query>;
318319fn load<'conn, 'query, T>(
320&'conn mut self,
321 source: T,
322 ) -> QueryResult<Self::Cursor<'conn, 'query>>
323where
324T: Query + QueryFragment<Self::Backend> + QueryId + 'query,
325Self::Backend: QueryMetadata<T::SqlType>,
326 {
327let statement = self.prepared_query(source)?;
328329Ok(StatementIterator::new(statement))
330 }
331}
332333impl WithMetadataLookupfor SqliteConnection {
334fn metadata_lookup(&mut self) -> &mut <Sqliteas TypeMetadata>::MetadataLookup {
335&mut self.metadata_lookup
336 }
337}
338339#[cfg(feature = "r2d2")]
340impl crate::r2d2::R2D2Connectionfor crate::sqlite::SqliteConnection {
341fn ping(&mut self) -> QueryResult<()> {
342use crate::RunQueryDsl;
343344crate::r2d2::CheckConnectionQuery.execute(self).map(|_| ())
345 }
346347fn is_broken(&mut self) -> bool {
348AnsiTransactionManager::is_broken_transaction_manager(self)
349 }
350}
351352impl MultiConnectionHelperfor SqliteConnection {
353fn to_any<'a>(
354 lookup: &mut <Self::Backend as crate::sql_types::TypeMetadata>::MetadataLookup,
355 ) -> &mut (dyn core::any::Any + 'a) {
356lookup357 }
358359fn from_any(
360 lookup: &mut dyn core::any::Any,
361 ) -> Option<&mut <Self::Backend as crate::sql_types::TypeMetadata>::MetadataLookup> {
362lookup.downcast_mut()
363 }
364}
365366/// The decision returned by an [`on_commit`](SqliteConnection::on_commit)
367/// callback, controlling whether a pending commit completes.
368#[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)]
369pub enum CommitDecision {
370/// Let the commit proceed normally.
371Proceed,
372/// Convert the commit into a rollback.
373Rollback,
374}
375376/// The decision returned by an [`on_progress`](SqliteConnection::on_progress)
377/// callback, controlling whether a long-running query keeps executing.
378#[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)]
379pub enum ProgressDecision {
380/// Let the query continue executing.
381Continue,
382/// Interrupt the query (causes `SQLITE_INTERRUPT`).
383Interrupt,
384}
385386/// The decision returned by an [`on_busy`](SqliteConnection::on_busy)
387/// callback when the database is locked.
388#[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)]
389pub enum BusyDecision {
390/// Retry the locked operation.
391Retry,
392/// Give up, returning `SQLITE_BUSY` to the caller.
393GiveUp,
394}
395396impl SqliteConnection {
397/// Run a transaction with `BEGIN IMMEDIATE`
398 ///
399 /// This method will return an error if a transaction is already open.
400 ///
401 /// # Example
402 ///
403 /// ```rust
404 /// # include!("../../doctest_setup.rs");
405 /// #
406 /// # fn main() {
407 /// # run_test().unwrap();
408 /// # }
409 /// #
410 /// # fn run_test() -> QueryResult<()> {
411 /// # let mut conn = SqliteConnection::establish(":memory:").unwrap();
412 /// conn.immediate_transaction(|conn| {
413 /// // Do stuff in a transaction
414 /// Ok(())
415 /// })
416 /// # }
417 /// ```
418pub fn immediate_transaction<T, E, F>(&mut self, f: F) -> Result<T, E>
419where
420F: FnOnce(&mut Self) -> Result<T, E>,
421 E: From<Error>,
422 {
423self.transaction_sql(f, "BEGIN IMMEDIATE")
424 }
425426/// Run a transaction with `BEGIN EXCLUSIVE`
427 ///
428 /// This method will return an error if a transaction is already open.
429 ///
430 /// # Example
431 ///
432 /// ```rust
433 /// # include!("../../doctest_setup.rs");
434 /// #
435 /// # fn main() {
436 /// # run_test().unwrap();
437 /// # }
438 /// #
439 /// # fn run_test() -> QueryResult<()> {
440 /// # let mut conn = SqliteConnection::establish(":memory:").unwrap();
441 /// conn.exclusive_transaction(|conn| {
442 /// // Do stuff in a transaction
443 /// Ok(())
444 /// })
445 /// # }
446 /// ```
447pub fn exclusive_transaction<T, E, F>(&mut self, f: F) -> Result<T, E>
448where
449F: FnOnce(&mut Self) -> Result<T, E>,
450 E: From<Error>,
451 {
452self.transaction_sql(f, "BEGIN EXCLUSIVE")
453 }
454455/// Returns the rowid of the most recent successful INSERT on this connection.
456 ///
457 /// Returns `None` if no successful INSERT into a rowid table has been performed
458 /// on this connection, and `Some(rowid)` otherwise.
459 ///
460 /// See [the SQLite documentation](https://www.sqlite.org/c3ref/last_insert_rowid.html)
461 /// for details.
462 ///
463 /// # Caveats
464 /// - Inserts into `WITHOUT ROWID` tables are not recorded
465 /// - Failed `INSERT` (constraint violations) do not change the value
466 /// - `INSERT OR REPLACE` always updates the value
467 /// - Within triggers, returns the rowid of the trigger's INSERT;
468 /// reverts after the trigger completes
469 ///
470 /// # Example
471 /// ```rust
472 /// # include!("../../doctest_setup.rs");
473 /// # fn main() {
474 /// # run_test().unwrap();
475 /// # }
476 /// # fn run_test() -> QueryResult<()> {
477 /// use core::num::NonZeroI64;
478 /// use diesel::connection::SimpleConnection;
479 /// let conn = &mut SqliteConnection::establish(":memory:").unwrap();
480 /// conn.batch_execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)")?;
481 /// conn.batch_execute("INSERT INTO users (name) VALUES ('Sean')")?;
482 /// let rowid = conn.last_insert_rowid();
483 /// assert_eq!(rowid, NonZeroI64::new(1));
484 /// conn.batch_execute("INSERT INTO users (name) VALUES ('Tess')")?;
485 /// let rowid = conn.last_insert_rowid();
486 /// assert_eq!(rowid, NonZeroI64::new(2));
487 /// # Ok(())
488 /// # }
489 /// ```
490pub fn last_insert_rowid(&self) -> Option<NonZeroI64> {
491NonZeroI64::new(self.raw_connection.last_insert_rowid())
492 }
493494fn transaction_sql<T, E, F>(&mut self, f: F, sql: &str) -> Result<T, E>
495where
496F: FnOnce(&mut Self) -> Result<T, E>,
497 E: From<Error>,
498 {
499 AnsiTransactionManager::begin_transaction_sql(&mut *self, sql)?;
500match f(&mut *self) {
501Ok(value) => {
502 AnsiTransactionManager::commit_transaction(&mut *self)?;
503Ok(value)
504 }
505Err(e) => {
506 AnsiTransactionManager::rollback_transaction(&mut *self)?;
507Err(e)
508 }
509 }
510 }
511512fn prepared_query<'conn, 'query, T>(
513&'conn mut self,
514 source: T,
515 ) -> QueryResult<StatementUse<'conn, 'query>>
516where
517T: QueryFragment<Sqlite> + QueryId + 'query,
518 {
519self.instrumentation
520 .on_connection_event(InstrumentationEvent::StartQuery {
521 query: &crate::debug_query(&source),
522 });
523let raw_connection = &self.raw_connection;
524let cache = &mut self.statement_cache;
525let statement = match cache.cached_statement(
526&source,
527&Sqlite,
528&[],
529raw_connection,
530Statement::prepare,
531&mut *self.instrumentation,
532 ) {
533Ok(statement) => statement,
534Err(e) => {
535self.instrumentation
536 .on_connection_event(InstrumentationEvent::FinishQuery {
537 query: &crate::debug_query(&source),
538 error: Some(&e),
539 });
540541return Err(e);
542 }
543 };
544545StatementUse::bind(statement, source, &mut *self.instrumentation)
546 }
547548/// Serialize the current SQLite database into a byte buffer.
549 ///
550 /// The serialized data is identical to the data that would be written to disk if the database
551 /// was saved in a file.
552 ///
553 /// # Returns
554 ///
555 /// This function returns a [`SerializedDatabase`] wrapping the serialized
556 /// bytes. If SQLite fails to allocate the buffer holding them, the failure
557 /// is reported by [`SerializedDatabase::try_as_slice`].
558pub fn serialize_database_to_buffer(&mut self) -> SerializedDatabase {
559self.raw_connection.serialize()
560 }
561562/// Deserialize an SQLite database from a byte buffer.
563 ///
564 /// This function takes a byte slice and attempts to deserialize it into a SQLite database.
565 /// If successful, the database is loaded into the connection. If the deserialization fails,
566 /// an error is returned.
567 ///
568 /// The database is opened in READONLY mode.
569 ///
570 /// # Example
571 ///
572 /// ```no_run
573 /// # use diesel::sqlite::SerializedDatabase;
574 /// # use diesel::sqlite::SqliteConnection;
575 /// # use diesel::result::QueryResult;
576 /// # use diesel::sql_query;
577 /// # use diesel::Connection;
578 /// # use diesel::RunQueryDsl;
579 /// # fn main() {
580 /// let connection = &mut SqliteConnection::establish(":memory:").unwrap();
581 ///
582 /// sql_query("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)")
583 /// .execute(connection).unwrap();
584 /// sql_query("INSERT INTO users (name, email) VALUES ('John Doe', 'john.doe@example.com'), ('Jane Doe', 'jane.doe@example.com')")
585 /// .execute(connection).unwrap();
586 ///
587 /// // Serialize the database to a byte vector
588 /// let serialized_db: SerializedDatabase = connection.serialize_database_to_buffer();
589 ///
590 /// // Create a new in-memory SQLite database
591 /// let connection = &mut SqliteConnection::establish(":memory:").unwrap();
592 ///
593 /// // Deserialize the byte vector into the new database
594 /// connection.deserialize_readonly_database_from_buffer(serialized_db.try_as_slice().unwrap()).unwrap();
595 /// #
596 /// # }
597 /// ```
598// TODO: Diesel 3.0 This signature needs to change, we want to expose more options (schema name, readonly)
599 // and also ensure that this is not as unsafe as the current construct anymore. Maybe just accept a owned buffer or static pointer
600 // only instead? (So `Cow<'static, [u8]>`?)
601#[allow(unsafe_code)]
602pub fn deserialize_readonly_database_from_buffer(&mut self, data: &[u8]) -> QueryResult<()> {
603// we copy the buffer here
604 // to make sure the underlying buffer lives as long as the connection
605self.serialized_data.push(data.to_vec());
606let last = self607 .serialized_data
608 .last()
609 .expect("We literally pushed it above, so it's there");
610unsafe {
611// SAFETY: We store the buffer inside of the connection and we never touch it until
612 // we drop the connection
613self.raw_connection.deserialize(last)
614 }
615 }
616617/// Provides temporary access to the raw SQLite database connection handle.
618 ///
619 /// This method provides a way to access the underlying `sqlite3` pointer,
620 /// enabling direct use of the SQLite C API for advanced features that
621 /// Diesel does not wrap, such as the [session extension](https://www.sqlite.org/sessionintro.html),
622 /// [hooks](https://www.sqlite.org/c3ref/update_hook.html), or other advanced APIs.
623 ///
624 /// # Why Diesel Doesn't Wrap These APIs
625 ///
626 /// Certain SQLite features, such as the session extension, are **optional** and only
627 /// available when SQLite is compiled with specific flags (e.g., `-DSQLITE_ENABLE_SESSION`
628 /// and `-DSQLITE_ENABLE_PREUPDATE_HOOK` for sessions). These compile-time options determine
629 /// whether the corresponding C API functions exist in the SQLite library's ABI.
630 ///
631 /// Because Diesel must work with any SQLite library at runtime—including system-provided
632 /// libraries that may lack these optional features—it **cannot safely provide wrappers**
633 /// for APIs that may or may not exist. Doing so would either:
634 ///
635 /// - Cause **linker errors** at compile time if the user's `libsqlite3-sys` wasn't compiled
636 /// with the required flags, or
637 /// - Cause **undefined behavior** at runtime if Diesel called functions that don't exist
638 /// in the linked library.
639 ///
640 /// While feature gates could theoretically solve this problem, Diesel already has an
641 /// extensive API surface with many existing feature combinations. Each new feature gate
642 /// adds a **combinatorial explosion** of test configurations that must be validated,
643 /// making the library increasingly difficult to maintain. Therefore, exposing the raw
644 /// connection is the preferred approach for niche SQLite features.
645 ///
646 /// By exposing the raw connection handle, Diesel allows users who **know** they have
647 /// access to a properly configured SQLite build to use these advanced features directly
648 /// through their own FFI bindings.
649 ///
650 /// # Safety
651 ///
652 /// This method is marked `unsafe` because improper use of the raw connection handle
653 /// can lead to undefined behavior. The caller must ensure that:
654 ///
655 /// - The connection handle is **not closed** during the callback.
656 /// - The connection handle is **not stored** beyond the callback's scope.
657 /// - Concurrent access rules are respected (SQLite connections are not thread-safe
658 /// unless using serialized threading mode).
659 /// - **Transaction state is not modified** — do not execute `BEGIN`, `COMMIT`,
660 /// `ROLLBACK`, or `SAVEPOINT` statements via the raw handle. Diesel's
661 /// [`AnsiTransactionManager`] tracks transaction nesting internally, and
662 /// bypassing it will cause Diesel's view of the transaction state to diverge
663 /// from SQLite's actual state.
664 /// - **Diesel's prepared statements are not disturbed** — do not call
665 /// `sqlite3_finalize()` or `sqlite3_reset()` on statements that belong to
666 /// Diesel's `StatementCache`. Doing so will cause use-after-free or
667 /// double-free when Diesel later accesses those statements.
668 ///
669 /// [`AnsiTransactionManager`]: crate::connection::AnsiTransactionManager
670 ///
671 /// # Example
672 ///
673 /// ```rust
674 /// use diesel::sqlite::SqliteConnection;
675 /// use diesel::Connection;
676 ///
677 /// let mut conn = SqliteConnection::establish(":memory:").unwrap();
678 ///
679 /// // SAFETY: We do not close or store the connection handle,
680 /// // and we do not modify Diesel-managed state (transactions, cached statements).
681 /// let is_valid = unsafe {
682 /// conn.with_raw_connection(|raw_conn| {
683 /// // The raw connection pointer can be passed to SQLite C API functions
684 /// // from your own `libsqlite3-sys` (native) or `sqlite-wasm-rs` (WASM)
685 /// // dependency — for example, `sqlite3_get_autocommit(raw_conn)` or
686 /// // `sqlite3session_create(raw_conn, ...)`.
687 /// !raw_conn.is_null()
688 /// })
689 /// };
690 /// assert!(is_valid);
691 /// ```
692 ///
693 /// # Platform Notes
694 ///
695 /// This method works identically on both native and WASM targets. However,
696 /// you must depend on the appropriate FFI crate for your target:
697 ///
698 /// - **Native**: Add `libsqlite3-sys` as a dependency
699 /// - **WASM** (`wasm32-unknown-unknown`): Add `sqlite-wasm-rs` as a dependency
700 ///
701 /// Both crates expose a compatible `sqlite3` type that can be used with the
702 /// pointer returned by this method.
703#[allow(unsafe_code)]
704pub unsafe fn with_raw_connection<R, F>(&mut self, f: F) -> R
705where
706F: FnOnce(*mut ffi::sqlite3) -> R,
707 {
708f(self.raw_connection.internal_connection.as_ptr())
709 }
710711/// Runs `f` with a borrowed `SqliteConnection` wrapping `db`, giving SQLite
712 /// callbacks the full connection API. Statements prepared during `f` are
713 /// finalized on return, but `db` is left open, since SQLite owns it.
714 ///
715 /// # Safety
716 ///
717 /// `db` must be a valid `sqlite3` handle that stays open for the duration
718 /// of the call.
719#[allow(unsafe_code)]
720pub(crate) unsafe fn with_borrowed_connection<R>(
721 db: core::ptr::NonNull<ffi::sqlite3>,
722 f: impl FnOnce(&mut SqliteConnection) -> R,
723 ) -> R {
724// Tears the borrowed connection down on every exit path, including a
725 // panic unwinding out of `f`.
726struct Borrowed(core::mem::ManuallyDrop<SqliteConnection>);
727728impl Dropfor Borrowed {
729fn drop(&mut self) {
730// SAFETY: `self.0` is not touched again after this take.
731let conn = unsafe { core::mem::ManuallyDrop::take(&mut self.0) };
732let SqliteConnection {
733 statement_cache,
734 raw_connection,
735 ..
736 } = conn;
737// Finalize prepared statements, but do not run `RawConnection`'s
738 // `Drop`, which would close a handle we do not own.
739drop(statement_cache);
740 core::mem::forget(raw_connection);
741 }
742 }
743744let mut conn = Borrowed(core::mem::ManuallyDrop::new(SqliteConnection {
745 statement_cache: StatementCache::new(),
746 raw_connection: RawConnection::from_ptr(db),
747 transaction_state: AnsiTransactionManager::default(),
748 metadata_lookup: (),
749 instrumentation: DynInstrumentation::default_instrumentation(),
750 serialized_data: Vec::new(),
751 }));
752753let result = f(&mut conn.0);
754755// The borrowed connection is discarded without committing or rolling
756 // back, so a transaction left open by `f` would leak onto the handle.
757if 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!(
758matches!(
759 AnsiTransactionManager::transaction_manager_status_mut(&mut *conn.0)
760 .transaction_depth(),
761Ok(None)
762 ),
763"callback must not leave an open transaction on the borrowed connection"
764);
765766result767 }
768769fn register_diesel_sql_functions(&self) -> QueryResult<()> {
770// When running under miri with a native libsqlite3 (see
771 // `-Zmiri-native-lib`), native code is not allowed to call back into
772 // Rust. Registering `diesel_manage_updated_at` hands a Rust function
773 // pointer to sqlite, whose `xDestroy` callback is invoked by native
774 // code when the connection is closed, which miri rejects. As miri is
775 // only used to run a subset of the test suite, we skip the registration
776 // entirely in that case.
777#[cfg(miri)]
778return Ok(());
779780#[cfg(not(miri))]
781{
782use crate::sql_types::{Integer, Text};
783784// This function has side effects (creates triggers), so it should not
785 // be deterministic. We use DIRECTONLY to prevent it from being called
786 // from malicious schema objects in untrusted databases.
787functions::register::<Text, Integer, _, _, _>(
788&self.raw_connection,
789"diesel_manage_updated_at",
790crate::sqlite::SqliteFunctionBehavior::DIRECTONLY,
791 |conn, table_name: String| {
792conn.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!(
793include_str!("diesel_manage_updated_at.sql"),
794 table_name = table_name
795 ))
796 .expect("Failed to create trigger");
7970 // have to return *something*
798},
799 )
800 }
801 }
802803fn establish_inner(database_url: &str) -> Result<SqliteConnection, ConnectionError> {
804use crate::result::ConnectionError::CouldntSetupConfiguration;
805let raw_connection = RawConnection::establish(database_url)?;
806let conn = Self {
807 statement_cache: StatementCache::new(),
808raw_connection,
809 transaction_state: AnsiTransactionManager::default(),
810 metadata_lookup: (),
811 instrumentation: DynInstrumentation::none(),
812 serialized_data: Vec::new(),
813 };
814 conn.register_diesel_sql_functions()
815 .map_err(CouldntSetupConfiguration)?;
816Ok(conn)
817 }
818}
819820fn error_message(err_code: libc::c_int) -> &'static str {
821 ffi::code_to_str(err_code)
822}
823824#[cfg(test)]
825mod tests {
826use super::*;
827use crate::dsl::sql;
828use crate::prelude::*;
829use crate::sql_types::{Integer, Text};
830#[cfg(not(miri))]
831use crate::test_helpers::format_error;
832833fn connection() -> SqliteConnection {
834 SqliteConnection::establish(":memory:").unwrap()
835 }
836837#[diesel_test_helper::test]
838 #[allow(unsafe_code)]
839fn with_raw_connection_can_return_values() {
840let connection = &mut connection();
841842// SAFETY: We only read connection status, which doesn't modify state.
843let autocommit_status = unsafe {
844 connection.with_raw_connection(|raw_conn| ffi::sqlite3_get_autocommit(raw_conn))
845 };
846847// Outside a transaction, autocommit should be enabled (returns non-zero)
848assert_ne!(autocommit_status, 0, "Expected autocommit to be enabled");
849 }
850851#[diesel_test_helper::test]
852 #[allow(unsafe_code)]
853fn with_raw_connection_works_after_diesel_operations() {
854let connection = &mut connection();
855856// First, do some Diesel operations
857crate::sql_query("CREATE TABLE test_table (id INTEGER PRIMARY KEY, value TEXT)")
858 .execute(connection)
859 .unwrap();
860crate::sql_query("INSERT INTO test_table (value) VALUES ('hello')")
861 .execute(connection)
862 .unwrap();
863864// SAFETY: We only read the last insert rowid, which is a read-only operation.
865let last_rowid = unsafe {
866 connection.with_raw_connection(|raw_conn| ffi::sqlite3_last_insert_rowid(raw_conn))
867 };
868869assert_eq!(last_rowid, 1, "Last insert rowid should be 1");
870871// Verify Diesel still works after using raw connection
872let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM test_table")
873 .get_result(connection)
874 .unwrap();
875assert_eq!(count, 1);
876 }
877878#[diesel_test_helper::test]
879// Registers a callback that is invoked by the native library, or reads memory
880 // allocated by it, which is not supported when running under miri with a native
881 // libsqlite3 (`-Zmiri-native-lib`).
882#[cfg(not(miri))] // ffi call
883#[allow(unsafe_code)]
884fn with_raw_connection_can_execute_raw_sql() {
885let connection = &mut connection();
886887// Create a table using Diesel first
888crate::sql_query("CREATE TABLE raw_test (id INTEGER PRIMARY KEY, name TEXT)")
889 .execute(connection)
890 .unwrap();
891892// SAFETY: We execute a simple INSERT via raw SQLite API.
893 // This modifies the database but in a way compatible with Diesel.
894let result = unsafe {
895 connection.with_raw_connection(|raw_conn| {
896let sql = c"INSERT INTO raw_test (name) VALUES ('from_raw')";
897let mut err_msg: *mut libc::c_char = core::ptr::null_mut();
898let rc = ffi::sqlite3_exec(
899 raw_conn,
900 sql.as_ptr(),
901None,
902 core::ptr::null_mut(),
903&mut err_msg,
904 );
905if rc != ffi::SQLITE_OK && !err_msg.is_null() {
906 ffi::sqlite3_free(err_msg as *mut libc::c_void);
907 }
908 rc
909 })
910 };
911912assert_eq!(result, ffi::SQLITE_OK, "Raw SQL execution should succeed");
913914// Verify the insert worked using Diesel
915let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM raw_test")
916 .get_result(connection)
917 .unwrap();
918assert_eq!(count, 1);
919920let name: String = sql::<Text>("SELECT name FROM raw_test WHERE id = 1")
921 .get_result(connection)
922 .unwrap();
923assert_eq!(name, "from_raw");
924 }
925926#[diesel_test_helper::test]
927 #[allow(unsafe_code)]
928fn with_raw_connection_works_within_transaction() {
929let connection = &mut connection();
930931crate::sql_query("CREATE TABLE txn_test (id INTEGER PRIMARY KEY, value INTEGER)")
932 .execute(connection)
933 .unwrap();
934935 connection
936 .transaction::<_, crate::result::Error, _>(|conn| {
937crate::sql_query("INSERT INTO txn_test (value) VALUES (42)")
938 .execute(conn)
939 .unwrap();
940941// SAFETY: We only read the autocommit status inside a transaction.
942let autocommit = unsafe {
943 conn.with_raw_connection(|raw_conn| ffi::sqlite3_get_autocommit(raw_conn))
944 };
945946// Inside a transaction, autocommit should be disabled (returns 0)
947assert_eq!(
948 autocommit, 0,
949"Autocommit should be disabled inside transaction"
950);
951952Ok(())
953 })
954 .unwrap();
955956// After transaction commits, autocommit should be re-enabled
957let autocommit = unsafe {
958 connection.with_raw_connection(|raw_conn| ffi::sqlite3_get_autocommit(raw_conn))
959 };
960assert_ne!(
961 autocommit, 0,
962"Autocommit should be enabled after transaction"
963);
964 }
965966#[diesel_test_helper::test]
967// Registers a callback that is invoked by the native library, or reads memory
968 // allocated by it, which is not supported when running under miri with a native
969 // libsqlite3 (`-Zmiri-native-lib`).
970#[cfg(not(miri))] // ffi call
971#[allow(unsafe_code)]
972fn with_raw_connection_can_read_database_filename() {
973let connection = &mut connection();
974975// SAFETY: We only read the database filename, which is a read-only operation.
976let filename = unsafe {
977 connection.with_raw_connection(|raw_conn| {
978let db_name = c"main";
979let filename_ptr = ffi::sqlite3_db_filename(raw_conn, db_name.as_ptr());
980if filename_ptr.is_null() {
981None
982} else {
983// For :memory: databases, this might return empty string or special value
984let cstr = core::ffi::CStr::from_ptr(filename_ptr);
985Some(cstr.to_string_lossy().into_owned())
986 }
987 })
988 };
989990// For in-memory databases, sqlite3_db_filename returns a non-null pointer
991 // to an empty string
992assert_eq!(
993 filename,
994Some(String::new()),
995"In-memory database filename should be an empty string"
996);
997 }
998999#[diesel_test_helper::test]
1000 #[allow(unsafe_code)]
1001fn with_raw_connection_changes_count() {
1002let connection = &mut connection();
10031004crate::sql_query("CREATE TABLE changes_test (id INTEGER PRIMARY KEY, value INTEGER)")
1005 .execute(connection)
1006 .unwrap();
10071008crate::sql_query("INSERT INTO changes_test (value) VALUES (1), (2), (3)")
1009 .execute(connection)
1010 .unwrap();
10111012// Update all rows using raw connection
1013let changes = unsafe {
1014 connection.with_raw_connection(|raw_conn| {
1015let sql = c"UPDATE changes_test SET value = value + 10";
1016let mut err_msg: *mut libc::c_char = core::ptr::null_mut();
1017let rc = ffi::sqlite3_exec(
1018 raw_conn,
1019 sql.as_ptr(),
1020None,
1021 core::ptr::null_mut(),
1022&mut err_msg,
1023 );
1024if rc != ffi::SQLITE_OK && !err_msg.is_null() {
1025 ffi::sqlite3_free(err_msg as *mut libc::c_void);
1026return -1;
1027 }
1028 ffi::sqlite3_changes(raw_conn)
1029 })
1030 };
10311032assert_eq!(changes, 3, "Should have updated 3 rows");
10331034// Verify the updates using Diesel
1035let values: Vec<i32> = sql::<Integer>("SELECT value FROM changes_test ORDER BY id")
1036 .load(connection)
1037 .unwrap();
1038assert_eq!(values, vec![11, 12, 13]);
1039 }
10401041// catch_unwind is not available in WASM (panic = "abort")
1042#[diesel_test_helper::test]
1043 #[allow(unsafe_code)]
1044 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
1045fn with_raw_connection_recovers_after_panic() {
1046let connection = &mut connection();
10471048crate::sql_query("CREATE TABLE panic_test (id INTEGER PRIMARY KEY, value TEXT)")
1049 .execute(connection)
1050 .unwrap();
10511052// Panic inside the callback
1053let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
1054 connection.with_raw_connection(|_raw_conn| {
1055panic!("intentional panic inside with_raw_connection");
1056 })
1057 }));
1058assert!(result.is_err(), "Should have caught the panic");
10591060// Connection should still be usable after the panic
1061crate::sql_query("INSERT INTO panic_test (value) VALUES ('after_panic')")
1062 .execute(connection)
1063 .unwrap();
10641065let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM panic_test")
1066 .get_result(connection)
1067 .unwrap();
1068assert_eq!(count, 1, "Connection should work after panic in callback");
1069 }
10701071// Filesystem access is not available in WASM
1072#[diesel_test_helper::test]
1073 #[allow(unsafe_code)]
1074// Registers a callback that is invoked by the native library, or reads memory
1075 // allocated by it, which is not supported when running under miri with a native
1076 // libsqlite3 (`-Zmiri-native-lib`).
1077#[cfg(not(miri))] // ffi call
1078#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
1079fn with_raw_connection_can_read_file_database_filename() {
1080let dir = std::env::temp_dir().join("diesel_test_filename.db");
1081let db_path = dir.to_str().unwrap();
10821083// Clean up from any previous run
1084let _ = std::fs::remove_file(db_path);
10851086let connection = &mut SqliteConnection::establish(db_path).unwrap();
10871088// SAFETY: We only read the database filename, which is a read-only operation.
1089let filename = unsafe {
1090 connection.with_raw_connection(|raw_conn| {
1091let db_name = c"main";
1092let filename_ptr = ffi::sqlite3_db_filename(raw_conn, db_name.as_ptr());
1093if filename_ptr.is_null() {
1094None
1095} else {
1096let cstr = core::ffi::CStr::from_ptr(filename_ptr);
1097Some(cstr.to_string_lossy().into_owned())
1098 }
1099 })
1100 };
11011102let filename = filename.expect("File-based database should have a filename");
1103assert!(
1104 filename.contains("diesel_test_filename.db"),
1105"Filename should contain the database name, got: {filename}"
1106);
11071108// Clean up
1109let _ = std::fs::remove_file(db_path);
1110 }
11111112// Registers a callback that is invoked by the native library, or reads memory
1113 // allocated by it, which is not supported when running under miri with a native
1114 // libsqlite3 (`-Zmiri-native-lib`).
1115#[cfg(not(miri))] // ffi call
1116#[diesel_test_helper::test]
1117fn database_serializes_and_deserializes_successfully() {
1118let expected_users = vec![
1119 (
11201,
1121"John Doe".to_string(),
1122"john.doe@example.com".to_string(),
1123 ),
1124 (
11252,
1126"Jane Doe".to_string(),
1127"jane.doe@example.com".to_string(),
1128 ),
1129 ];
11301131let conn1 = &mut connection();
1132let _ =
1133crate::sql_query("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)")
1134 .execute(conn1);
1135let _ = crate::sql_query("INSERT INTO users (name, email) VALUES ('John Doe', 'john.doe@example.com'), ('Jane Doe', 'jane.doe@example.com')")
1136 .execute(conn1);
11371138for _i in 0..2 {
1139let serialized_database = conn1.serialize_database_to_buffer();
1140let conn2 = &mut connection();
1141 conn2
1142 .deserialize_readonly_database_from_buffer(
1143 serialized_database.try_as_slice().unwrap(),
1144 )
1145 .unwrap();
11461147let query =
1148 sql::<(Integer, Text, Text)>("SELECT id, name, email FROM users ORDER BY id");
1149let actual_users = query.load::<(i32, String, String)>(conn2).unwrap();
11501151assert_eq!(expected_users, actual_users);
1152// drop the database here
1153 // and requery the database to make sure the database owns
1154 // required data
1155std::mem::drop(serialized_database);
1156let query =
1157 sql::<(Integer, Text, Text)>("SELECT id, name, email FROM users ORDER BY id");
1158let actual_users = query.load::<(i32, String, String)>(conn2).unwrap();
11591160assert_eq!(expected_users, actual_users);
1161 }
1162 }
11631164// Registers a callback that is invoked by the native library, or reads memory
1165 // allocated by it, which is not supported when running under miri with a native
1166 // libsqlite3 (`-Zmiri-native-lib`).
1167#[cfg(not(miri))] // ffi call
1168#[diesel_test_helper::test]
1169fn database_deserialize_random_bytes() {
1170let buffer = vec![0, 1, 2, 3, 4];
1171let conn = &mut SqliteConnection::establish(":memory:").unwrap();
11721173 conn.deserialize_readonly_database_from_buffer(&buffer)
1174 .unwrap();
11751176let r = sql::<Integer>("SELECT id FROM users").load::<i32>(conn);
11771178assert!(r.is_err());
1179assert_eq!(format_error(&r.unwrap_err()), "file is not a database");
11801181let conn = &mut SqliteConnection::establish(":memory:").unwrap();
11821183let _ =
1184crate::sql_query("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)")
1185 .execute(conn);
1186let _ = crate::sql_query("INSERT INTO users (name, email) VALUES ('John Doe', 'john.doe@example.com'), ('Jane Doe', 'jane.doe@example.com')")
1187 .execute(conn);
11881189let db = conn.serialize_database_to_buffer();
1190// only get a valid header, but append garbage
1191let mut bad_buffer = db[..100].to_vec();
1192 bad_buffer.extend(b"whatever");
1193 conn.deserialize_readonly_database_from_buffer(&bad_buffer)
1194 .unwrap();
11951196let r = sql::<Integer>("SELECT id FROM users").load::<i32>(conn);
11971198assert!(r.is_err());
1199assert_eq!(
1200 format_error(&r.unwrap_err()),
1201"database disk image is malformed"
1202);
12031204// only get a valid header, but append garbage
1205let mut size_fitting_bad_buffer = db[..100].to_vec();
1206 size_fitting_bad_buffer.extend(
1207 core::iter::repeat(b"abcdefghij")
1208 .flatten()
1209 .take(db.len() - 100),
1210 );
1211let r = conn.deserialize_readonly_database_from_buffer(&size_fitting_bad_buffer);
12121213assert!(r.is_err());
1214assert_eq!(
1215 format_error(&r.unwrap_err()),
1216"database disk image is malformed"
1217);
1218 }
12191220#[diesel_test_helper::test]
1221fn database_serializes_empty_deserialized_database() {
1222let conn = &mut SqliteConnection::establish(":memory:").unwrap();
1223 conn.deserialize_readonly_database_from_buffer(&[]).unwrap();
12241225let serialized = conn.serialize_database_to_buffer();
12261227assert!(serialized.is_empty());
1228assert!(serialized.try_as_slice().unwrap().is_empty());
1229 }
12301231#[cfg(all(
1232 feature = "std",
1233 not(all(target_family = "wasm", target_os = "unknown")),
1234// These tests read memory allocated by the native library or rely on
1235 // native code invoking a panic hook, neither of which is supported when
1236 // running under miri with a native libsqlite3 (`-Zmiri-native-lib`).
1237not(miri)
1238 ))]
1239 #[allow(unsafe_code)]
1240mod sqlite_serialize_oom {
1241use super::super::oom_test_support::{panic_message, run_in_child, with_heap_limit};
1242use super::super::{SerializedDatabase, ffi};
1243use crate::connection::{Connection, SimpleConnection};
1244use crate::sqlite::SqliteConnection;
1245use crate::test_helpers::format_error;
12461247const MIN_DATABASE_BYTES: i64 = 1_048_576;
12481249// 64 KiB covers statement setup but cannot hold the 1 MiB serialization,
1250 // pinning the failure to output allocation after SQLite reports its size.
1251fn with_failing_serialize<R>(f: impl FnOnce() -> R) -> R {
1252 with_heap_limit(65_536, f)
1253 }
12541255#[test]
1256fn sqlite_serialize_oom_is_contained() {
1257 run_in_child(|| {
1258let mut conn = large_database();
12591260let (baseline_size, baseline) = serialize_direct(&conn);
1261assert!(
1262 baseline_size >= MIN_DATABASE_BYTES,
1263"the serialized database is smaller than 1 MiB"
1264);
1265assert!(
1266 !baseline.is_null(),
1267"SQLite refused to serialize a valid database"
1268);
1269// SAFETY: `sqlite3_serialize` returned this buffer and no wrapper owns it.
1270unsafe { ffi::sqlite3_free(baseline as _) };
12711272let (reported_size, data) = with_failing_serialize(|| serialize_direct(&conn));
1273if !data.is_null() {
1274// SAFETY: `sqlite3_serialize` returned this buffer and no wrapper owns it.
1275unsafe { ffi::sqlite3_free(data as _) };
1276 }
1277assert!(
1278 data.is_null(),
1279"SQLite did not fail the output allocation of the serialization"
1280);
1281// SQLite reports the required size before attempting output allocation.
1282assert!(
1283 reported_size >= MIN_DATABASE_BYTES,
1284"SQLite reported a serialization size of {reported_size} with a null buffer"
1285);
12861287let serialized: SerializedDatabase =
1288 with_failing_serialize(|| conn.serialize_database_to_buffer());
1289let error = serialized
1290 .try_as_slice()
1291 .expect_err("the failed output allocation must surface as an error");
1292assert_eq!(format_error(&error), "out of memory");
12931294let payload = std::panic::catch_unwind(core::panic::AssertUnwindSafe(|| {
1295 core::hint::black_box(serialized[0]);
1296 }))
1297 .expect_err("the serialized database access did not panic");
1298let message = panic_message(&*payload);
1299assert!(
1300 message.contains("Cannot access the serialized database: out of memory"),
1301"SQLite serialization allocation failure surfaced as `{message}` instead \
1302 of a caught allocation panic"
1303);
1304 });
1305 }
13061307fn large_database() -> SqliteConnection {
1308let mut conn = SqliteConnection::establish(":memory:").unwrap();
1309 conn.batch_execute(&format!(
1310"CREATE TABLE blobs (id INTEGER PRIMARY KEY, payload BLOB);
1311 INSERT INTO blobs (payload) VALUES (zeroblob({MIN_DATABASE_BYTES}));"
1312))
1313 .unwrap();
1314 conn
1315 }
13161317fn serialize_direct(conn: &SqliteConnection) -> (ffi::sqlite3_int64, *mut u8) {
1318// SAFETY: The connection is live, a null schema selects `main`, and `size` is a writable out-parameter.
1319unsafe {
1320let mut size: ffi::sqlite3_int64 = 0;
1321let data = ffi::sqlite3_serialize(
1322 conn.raw_connection.internal_connection.as_ptr(),
1323 core::ptr::null(),
1324&mut size as *mut _,
13250,
1326 );
1327 (size, data)
1328 }
1329 }
1330 }
13311332// regression test for https://github.com/diesel-rs/diesel/issues/3425
1333#[diesel_test_helper::test]
1334fn test_correct_serialization_of_owned_strings() {
1335use crate::prelude::*;
13361337#[derive(Debug, crate::expression::AsExpression)]
1338 #[diesel(sql_type = diesel::sql_types::Text)]
1339struct CustomWrapper(String);
13401341impl crate::serialize::ToSql<Text, Sqlite> for CustomWrapper {
1342fn to_sql<'b>(
1343&'b self,
1344 out: &mut crate::serialize::Output<'b, '_, Sqlite>,
1345 ) -> crate::serialize::Result {
1346 out.set_value(self.0.to_string());
1347Ok(crate::serialize::IsNull::No)
1348 }
1349 }
13501351let connection = &mut connection();
13521353let res = crate::select(
1354 CustomWrapper("".into())
1355 .into_sql::<crate::sql_types::Text>()
1356 .nullable(),
1357 )
1358 .get_result::<Option<String>>(connection)
1359 .unwrap();
1360assert_eq!(res, Some(String::new()));
1361 }
13621363#[diesel_test_helper::test]
1364fn test_correct_serialization_of_owned_bytes() {
1365use crate::prelude::*;
13661367#[derive(Debug, crate::expression::AsExpression)]
1368 #[diesel(sql_type = diesel::sql_types::Binary)]
1369struct CustomWrapper(Vec<u8>);
13701371impl crate::serialize::ToSql<crate::sql_types::Binary, Sqlite> for CustomWrapper {
1372fn to_sql<'b>(
1373&'b self,
1374 out: &mut crate::serialize::Output<'b, '_, Sqlite>,
1375 ) -> crate::serialize::Result {
1376 out.set_value(self.0.clone());
1377Ok(crate::serialize::IsNull::No)
1378 }
1379 }
13801381let connection = &mut connection();
13821383let res = crate::select(
1384 CustomWrapper(Vec::new())
1385 .into_sql::<crate::sql_types::Binary>()
1386 .nullable(),
1387 )
1388 .get_result::<Option<Vec<u8>>>(connection)
1389 .unwrap();
1390assert_eq!(res, Some(Vec::new()));
1391 }
13921393#[diesel_test_helper::test]
1394fn correctly_handle_empty_query() {
1395let check_empty_query_error = |r: crate::QueryResult<usize>| {
1396assert!(r.is_err());
1397let err = r.unwrap_err();
1398assert!(
1399matches!(err, crate::result::Error::QueryBuilderError(ref b) if b.is::<crate::result::EmptyQuery>()),
1400"Expected a query builder error, but got {err}"
1401);
1402 };
1403let connection = &mut SqliteConnection::establish(":memory:").unwrap();
1404 check_empty_query_error(crate::sql_query("").execute(connection));
1405 check_empty_query_error(crate::sql_query(" ").execute(connection));
1406 check_empty_query_error(crate::sql_query("\n\t").execute(connection));
1407 check_empty_query_error(crate::sql_query("-- SELECT 1;").execute(connection));
1408 }
14091410#[diesel_test_helper::test]
1411fn last_insert_rowid_returns_none_on_fresh_connection() {
1412let conn = &mut connection();
1413assert_eq!(conn.last_insert_rowid(), None);
1414 }
14151416#[diesel_test_helper::test]
1417fn last_insert_rowid_returns_rowid_after_insert() {
1418let conn = &mut connection();
1419crate::sql_query("CREATE TABLE li_test (id INTEGER PRIMARY KEY, val TEXT NOT NULL)")
1420 .execute(conn)
1421 .unwrap();
14221423crate::sql_query("INSERT INTO li_test (val) VALUES ('a')")
1424 .execute(conn)
1425 .unwrap();
1426assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
14271428crate::sql_query("INSERT INTO li_test (val) VALUES ('b')")
1429 .execute(conn)
1430 .unwrap();
1431assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(2));
1432 }
14331434// Registers a callback that is invoked by the native library, or reads memory
1435 // allocated by it, which is not supported when running under miri with a native
1436 // libsqlite3 (`-Zmiri-native-lib`).
1437#[cfg(not(miri))] // ffi call
1438#[diesel_test_helper::test]
1439fn last_insert_rowid_unchanged_after_failed_insert() {
1440let conn = &mut connection();
1441crate::sql_query(
1442"CREATE TABLE li_test2 (id INTEGER PRIMARY KEY, val TEXT NOT NULL UNIQUE)",
1443 )
1444 .execute(conn)
1445 .unwrap();
14461447crate::sql_query("INSERT INTO li_test2 (val) VALUES ('a')")
1448 .execute(conn)
1449 .unwrap();
1450let rowid = conn.last_insert_rowid();
1451assert_eq!(rowid, NonZeroI64::new(1));
14521453// This should fail due to UNIQUE constraint
1454let result = crate::sql_query("INSERT INTO li_test2 (val) VALUES ('a')").execute(conn);
1455assert!(result.is_err());
14561457// rowid should be unchanged
1458assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
1459 }
14601461#[diesel_test_helper::test]
1462fn last_insert_rowid_with_explicit_rowid() {
1463let conn = &mut connection();
1464crate::sql_query("CREATE TABLE li_test3 (id INTEGER PRIMARY KEY, val TEXT NOT NULL)")
1465 .execute(conn)
1466 .unwrap();
14671468crate::sql_query("INSERT INTO li_test3 (id, val) VALUES (42, 'a')")
1469 .execute(conn)
1470 .unwrap();
1471assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(42));
1472 }
14731474#[diesel_test_helper::test]
1475fn last_insert_rowid_unchanged_after_delete_and_update() {
1476let conn = &mut connection();
1477crate::sql_query("CREATE TABLE li_test4 (id INTEGER PRIMARY KEY, val TEXT NOT NULL)")
1478 .execute(conn)
1479 .unwrap();
14801481crate::sql_query("INSERT INTO li_test4 (val) VALUES ('a')")
1482 .execute(conn)
1483 .unwrap();
1484let rowid = conn.last_insert_rowid();
1485assert_eq!(rowid, NonZeroI64::new(1));
14861487crate::sql_query("UPDATE li_test4 SET val = 'b' WHERE id = 1")
1488 .execute(conn)
1489 .unwrap();
1490assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
14911492crate::sql_query("DELETE FROM li_test4 WHERE id = 1")
1493 .execute(conn)
1494 .unwrap();
1495assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
1496 }
14971498#[diesel_test_helper::test]
1499fn test_injection() {
1500diesel::table! {
1501#[sql_name = "quote'table"]
1502quote_table (id) {
1503 id -> Nullable<Integer>,
1504 name -> Nullable<Text>,
1505 }
1506 }
15071508let mut conn = SqliteConnection::establish(":memory:").unwrap();
15091510 conn.batch_execute("CREATE TABLE \"quote'table\" (id INTEGER PRIMARY KEY, name TEXT);")
1511 .unwrap();
15121513 diesel::insert_into(quote_table::table)
1514 .values((quote_table::id.eq(1), quote_table::name.eq("Jane")))
1515 .execute(&mut conn)
1516 .unwrap();
15171518if cfg!(not(miri)) {
1519// string access over ffi
1520let data = quote_table::table
1521 .load::<(Option<i32>, Option<String>)>(&mut conn)
1522 .unwrap();
1523assert_eq!(data, [(Some(1), Some("Jane".to_owned()))]);
1524 }
1525 }
1526}