Skip to main content

diesel/sqlite/connection/
mod.rs

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
7pub mod authorizer;
8mod bind_collector;
9mod collation_needed;
10mod functions;
11mod hooks;
12mod limits;
13mod owned_row;
14mod raw;
15mod row;
16mod serialized_database;
17pub(in crate::sqlite) mod sqlite_blob;
18mod sqlite_value;
19mod statement_iterator;
20mod stmt;
21mod trace;
22mod update_hook;
23
24pub use self::authorizer::{AuthorizerContext, AuthorizerDecision};
25pub use self::bind_collector::SqliteBindCollector;#[diesel_derives::__diesel_public_if(
26    feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
27)]
28pub(in crate::sqlite) use self::bind_collector::SqliteBindCollector;
29pub use self::bind_collector::SqliteBindValue;
30#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
31pub use self::bind_collector::{OwnedSqliteBindValue, SqliteBindCollectorData, SqliteBindValueRef};
32pub use self::collation_needed::{CollationNeededContext, SqliteTextRep};
33pub use self::limits::SqliteLimit;
34use self::raw::RawConnection;
35pub use self::serialized_database::SerializedDatabase;
36pub use self::sqlite_value::SqliteValue;
37use self::statement_iterator::*;
38use self::stmt::{Statement, StatementUse};
39pub use self::trace::{SqliteTraceEvent, SqliteTraceFlags};
40pub use self::update_hook::{
41    SqliteChangeEvent, SqliteChangeOp, SqliteChangeOps, SqliteUpdateRouter,
42};
43use super::SqliteAggregateFunction;
44use crate::connection::instrumentation::{DynInstrumentation, StrQueryHelper};
45use crate::connection::statement_cache::StatementCache;
46use crate::connection::*;
47use crate::deserialize::{FromSqlRow, StaticallySizedRow};
48use crate::expression::QueryMetadata;
49use crate::query_builder::*;
50use crate::query_dsl::RunQueryDslSupport;
51use crate::query_source::{ColumnHasTable, NamedTable};
52use crate::result::*;
53use crate::serialize::ToSql;
54use crate::sql_types::{HasSqlType, TypeMetadata};
55use crate::sqlite::{Sqlite, SqliteFunctionBehavior};
56use alloc::string::String;
57use alloc::string::ToString;
58use alloc::vec::Vec;
59use core::ffi as libc;
60use core::marker::PhantomData;
61use core::num::NonZeroI64;
62
63/// Connections for the SQLite backend. Unlike other backends, SQLite supported
64/// connection URLs are:
65///
66/// - File paths (`test.db`)
67/// - [URIs](https://sqlite.org/uri.html) (`file://test.db`)
68/// - Special identifiers (`:memory:`)
69///
70/// # Supported loading model implementations
71///
72/// * [`DefaultLoadingMode`]
73///
74/// As `SqliteConnection` only supports a single loading mode implementation,
75/// it is **not required** to explicitly specify a loading mode
76/// when calling [`RunQueryDsl::load_iter()`] or [`LoadConnection::load`]
77///
78/// [`RunQueryDsl::load_iter()`]: crate::query_dsl::RunQueryDsl::load_iter
79///
80/// ## DefaultLoadingMode
81///
82/// `SqliteConnection` only supports a single loading mode, which loads
83/// values row by row from the result set.
84///
85/// ```rust
86/// # include!("../../doctest_setup.rs");
87/// #
88/// # fn main() {
89/// #     run_test().unwrap();
90/// # }
91/// #
92/// # fn run_test() -> QueryResult<()> {
93/// #     use schema::users;
94/// #     let connection = &mut establish_connection();
95/// use diesel::connection::DefaultLoadingMode;
96/// {
97///     // scope to restrict the lifetime of the iterator
98///     let iter1 = users::table.load_iter::<(i32, String), DefaultLoadingMode>(connection)?;
99///
100///     for r in iter1 {
101///         let (id, name) = r?;
102///         println!("Id: {} Name: {}", id, name);
103///     }
104/// }
105///
106/// // works without specifying the loading mode
107/// let iter2 = users::table.load_iter::<(i32, String), _>(connection)?;
108///
109/// for r in iter2 {
110///     let (id, name) = r?;
111///     println!("Id: {} Name: {}", id, name);
112/// }
113/// #   Ok(())
114/// # }
115/// ```
116///
117/// This mode does **not support** creating
118/// multiple iterators using the same connection.
119///
120/// ```compile_fail
121/// # include!("../../doctest_setup.rs");
122/// #
123/// # fn main() {
124/// #     run_test().unwrap();
125/// # }
126/// #
127/// # fn run_test() -> QueryResult<()> {
128/// #     use schema::users;
129/// #     let connection = &mut establish_connection();
130/// use diesel::connection::DefaultLoadingMode;
131///
132/// let iter1 = users::table.load_iter::<(i32, String), DefaultLoadingMode>(connection)?;
133/// let iter2 = users::table.load_iter::<(i32, String), DefaultLoadingMode>(connection)?;
134///
135/// for r in iter1 {
136///     let (id, name) = r?;
137///     println!("Id: {} Name: {}", id, name);
138/// }
139///
140/// for r in iter2 {
141///     let (id, name) = r?;
142///     println!("Id: {} Name: {}", id, name);
143/// }
144/// #   Ok(())
145/// # }
146/// ```
147///
148/// # Concurrency
149///
150/// By default, when running into a database lock, the operation will abort with a
151/// `Database locked` error. However, it's possible to configure it for greater concurrency,
152/// trading latency for not having to deal with retries yourself.
153///
154/// You can use this example as blue-print for which statements to run after establishing a connection.
155/// It is **important** to run each `PRAGMA` in a single statement to make sure all of them apply
156/// correctly. In addition the order of the `PRAGMA` statements is relevant to prevent timeout
157/// issues for the later `PRAGMA` statements.
158///
159/// ```rust
160/// # include!("../../doctest_setup.rs");
161/// #
162/// # fn main() {
163/// #     run_test().unwrap();
164/// # }
165/// #
166/// # fn run_test() -> QueryResult<()> {
167/// #     use schema::users;
168/// use diesel::connection::SimpleConnection;
169/// let conn = &mut establish_connection();
170/// // see https://fractaledmind.github.io/2023/09/07/enhancing-rails-sqlite-fine-tuning/
171/// // sleep if the database is busy, this corresponds to up to 2 seconds sleeping time.
172/// conn.batch_execute("PRAGMA busy_timeout = 2000;")?;
173/// // better write-concurrency
174/// conn.batch_execute("PRAGMA journal_mode = WAL;")?;
175/// // fsync only in critical moments
176/// conn.batch_execute("PRAGMA synchronous = NORMAL;")?;
177/// // write WAL changes back every 1000 pages, for an in average 1MB WAL file.
178/// // May affect readers if number is increased
179/// conn.batch_execute("PRAGMA wal_autocheckpoint = 1000;")?;
180/// // free some space by truncating possibly massive WAL files from the last run
181/// conn.batch_execute("PRAGMA wal_checkpoint(TRUNCATE);")?;
182/// #   Ok(())
183/// # }
184/// ```
185#[allow(missing_debug_implementations)]
186#[cfg(feature = "__sqlite-shared")]
187pub struct SqliteConnection {
188    // statement_cache needs to be before raw_connection
189    // otherwise we will get errors about open statements before closing the
190    // connection itself
191    statement_cache: StatementCache<Sqlite, Statement>,
192    raw_connection: RawConnection,
193    transaction_state: AnsiTransactionManager,
194    // this exists for the sole purpose of implementing `WithMetadataLookup` trait
195    // and avoiding static mut which will be deprecated in 2024 edition
196    metadata_lookup: (),
197    instrumentation: DynInstrumentation,
198    // We potentially need to store a serialized
199    // database in here to make sure the database bytes
200    // live as long as the connection
201    // This is used by SqliteConnection::deserialize_readonly_database_from_buffer
202    // only
203    // This field needs to come after the RawConnection
204    // as we need to make sure the data are still there until the
205    // connection is dropped
206    //
207    // We are not allowed to modify the inner buffer until the database connection is dropped
208    serialized_data: Vec<Vec<u8>>,
209}
210
211// This relies on the invariant that RawConnection or Statement are never
212// leaked. If a reference to one of those was held on a different thread, this
213// would not be thread safe.
214#[allow(unsafe_code)]
215unsafe impl Send for SqliteConnection {}
216
217impl SimpleConnection for SqliteConnection {
218    fn batch_execute(&mut self, query: &str) -> QueryResult<()> {
219        self.instrumentation
220            .on_connection_event(InstrumentationEvent::StartQuery {
221                query: &StrQueryHelper::new(query),
222            });
223        let resp = self.raw_connection.exec(query);
224        self.instrumentation
225            .on_connection_event(InstrumentationEvent::FinishQuery {
226                query: &StrQueryHelper::new(query),
227                error: resp.as_ref().err(),
228            });
229        resp
230    }
231}
232
233impl ConnectionSealed for SqliteConnection {}
234
235impl Connection for SqliteConnection {
236    type Backend = Sqlite;
237    type TransactionManager = AnsiTransactionManager;
238
239    /// Establish a connection to the database specified by `database_url`.
240    ///
241    /// See [SqliteConnection] for supported `database_url`.
242    ///
243    /// If the database does not exist, this method will try to
244    /// create a new database and then establish a connection to it.
245    ///
246    /// ## WASM support
247    ///
248    /// If you plan to use this connection type on the `wasm32-unknown-unknown` target please
249    /// make sure to read the following notes:
250    ///
251    /// * The database is stored in memory by default.
252    /// * Persistent VFS (Virtual File Systems) is optional,
253    ///   see <https://github.com/Spxg/sqlite-wasm-rs> for details
254    fn establish(database_url: &str) -> ConnectionResult<Self> {
255        let mut instrumentation = DynInstrumentation::default_instrumentation();
256        instrumentation.on_connection_event(InstrumentationEvent::StartEstablishConnection {
257            url: database_url,
258        });
259
260        let establish_result = Self::establish_inner(database_url);
261        instrumentation.on_connection_event(InstrumentationEvent::FinishEstablishConnection {
262            url: database_url,
263            error: establish_result.as_ref().err(),
264        });
265        let mut conn = establish_result?;
266        conn.instrumentation = instrumentation;
267        Ok(conn)
268    }
269
270    fn execute_returning_count<T>(&mut self, source: &T) -> QueryResult<usize>
271    where
272        T: QueryFragment<Self::Backend> + QueryId,
273    {
274        let statement_use = self.prepared_query(source)?;
275        statement_use.run().and_then(|_| {
276            self.raw_connection
277                .rows_affected_by_last_query()
278                .map_err(Error::DeserializationError)
279        })
280    }
281
282    fn transaction_state(&mut self) -> &mut AnsiTransactionManager
283    where
284        Self: Sized,
285    {
286        &mut self.transaction_state
287    }
288
289    fn instrumentation(&mut self) -> &mut dyn Instrumentation {
290        &mut *self.instrumentation
291    }
292
293    fn set_instrumentation(&mut self, instrumentation: impl Instrumentation) {
294        self.instrumentation = instrumentation.into();
295    }
296
297    fn set_prepared_statement_cache_size(&mut self, size: CacheSize) {
298        self.statement_cache.set_cache_size(size);
299    }
300}
301
302impl LoadConnection<DefaultLoadingMode> for SqliteConnection {
303    type Cursor<'conn, 'query> = StatementIterator<'conn, 'query>;
304    type Row<'conn, 'query> = self::row::SqliteRow<'conn, 'query>;
305
306    fn load<'conn, 'query, T>(
307        &'conn mut self,
308        source: T,
309    ) -> QueryResult<Self::Cursor<'conn, 'query>>
310    where
311        T: Query + QueryFragment<Self::Backend> + QueryId + 'query,
312        Self::Backend: QueryMetadata<T::SqlType>,
313    {
314        let statement = self.prepared_query(source)?;
315
316        Ok(StatementIterator::new(statement))
317    }
318}
319
320impl WithMetadataLookup for SqliteConnection {
321    fn metadata_lookup(&mut self) -> &mut <Sqlite as TypeMetadata>::MetadataLookup {
322        &mut self.metadata_lookup
323    }
324}
325
326#[cfg(feature = "r2d2")]
327impl crate::r2d2::R2D2Connection for crate::sqlite::SqliteConnection {
328    fn ping(&mut self) -> QueryResult<()> {
329        use crate::RunQueryDsl;
330
331        crate::r2d2::CheckConnectionQuery.execute(self).map(|_| ())
332    }
333
334    fn is_broken(&mut self) -> bool {
335        AnsiTransactionManager::is_broken_transaction_manager(self)
336    }
337}
338
339impl MultiConnectionHelper for SqliteConnection {
340    fn to_any<'a>(
341        lookup: &mut <Self::Backend as crate::sql_types::TypeMetadata>::MetadataLookup,
342    ) -> &mut (dyn core::any::Any + 'a) {
343        lookup
344    }
345
346    fn from_any(
347        lookup: &mut dyn core::any::Any,
348    ) -> Option<&mut <Self::Backend as crate::sql_types::TypeMetadata>::MetadataLookup> {
349        lookup.downcast_mut()
350    }
351}
352
353/// The decision returned by an [`on_commit`](SqliteConnection::on_commit)
354/// callback, controlling whether a pending commit completes.
355#[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]
impl ::core::clone::Clone for CommitDecision {
    #[inline]
    fn clone(&self) -> CommitDecision { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for CommitDecision { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for CommitDecision {
    #[inline]
    fn eq(&self, other: &CommitDecision) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for CommitDecision {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {}
}Eq)]
356pub enum CommitDecision {
357    /// Let the commit proceed normally.
358    Proceed,
359    /// Convert the commit into a rollback.
360    Rollback,
361}
362
363/// The decision returned by an [`on_progress`](SqliteConnection::on_progress)
364/// callback, controlling whether a long-running query keeps executing.
365#[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]
impl ::core::clone::Clone for ProgressDecision {
    #[inline]
    fn clone(&self) -> ProgressDecision { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for ProgressDecision { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for ProgressDecision {
    #[inline]
    fn eq(&self, other: &ProgressDecision) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for ProgressDecision {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {}
}Eq)]
366pub enum ProgressDecision {
367    /// Let the query continue executing.
368    Continue,
369    /// Interrupt the query (causes `SQLITE_INTERRUPT`).
370    Interrupt,
371}
372
373/// The decision returned by an [`on_busy`](SqliteConnection::on_busy)
374/// callback when the database is locked.
375#[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]
impl ::core::clone::Clone for BusyDecision {
    #[inline]
    fn clone(&self) -> BusyDecision { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for BusyDecision { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for BusyDecision {
    #[inline]
    fn eq(&self, other: &BusyDecision) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for BusyDecision {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {}
}Eq)]
376pub enum BusyDecision {
377    /// Retry the locked operation.
378    Retry,
379    /// Give up, returning `SQLITE_BUSY` to the caller.
380    GiveUp,
381}
382
383/// The `auto_vacuum` mode of a database, controlling whether and when SQLite
384/// reclaims freed pages back to the file.
385///
386/// The mode is stored in the database file, not the connection. [`Full`] and
387/// [`Incremental`] can be switched between at any time, but changing from or to
388/// [`None`] only takes effect on a database with no tables yet, or after a
389/// subsequent `VACUUM` rewrites the file.
390///
391/// [`None`]: AutoVacuumMode::None
392/// [`Full`]: AutoVacuumMode::Full
393/// [`Incremental`]: AutoVacuumMode::Incremental
394#[derive(#[automatically_derived]
impl ::core::fmt::Debug for AutoVacuumMode {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                AutoVacuumMode::None => "None",
                AutoVacuumMode::Full => "Full",
                AutoVacuumMode::Incremental => "Incremental",
            })
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for AutoVacuumMode {
    #[inline]
    fn clone(&self) -> AutoVacuumMode { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for AutoVacuumMode { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for AutoVacuumMode {
    #[inline]
    fn eq(&self, other: &AutoVacuumMode) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for AutoVacuumMode {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {}
}Eq, const _: () =
    {
        use diesel;
        impl<__DB>
            diesel::deserialize::FromSql<crate::sql_types::Integer, __DB> for
            AutoVacuumMode where __DB: diesel::backend::Backend,
            crate::sql_types::Integer: diesel::sql_types::EnumSqlType<true,
            __DB>,
            <crate::sql_types::Integer as
            diesel::sql_types::EnumSqlType<true,
            __DB>>::Strategy: diesel::internal::derives::enum_::EnumMapping<__DB>
            {
            fn from_sql(value:
                    <__DB as diesel::backend::Backend>::RawValue<'_>)
                -> diesel::deserialize::Result<Self> {
                const VARIANTS:
                    &[diesel::internal::derives::enum_::EnumVariant] =
                    &[diesel::internal::derives::enum_::EnumVariant {
                                    discriminant: 0i128,
                                    rust_name: "None",
                                    sql_name: "None",
                                },
                                diesel::internal::derives::enum_::EnumVariant {
                                    discriminant: 1i128,
                                    rust_name: "Full",
                                    sql_name: "Full",
                                },
                                diesel::internal::derives::enum_::EnumVariant {
                                    discriminant: 2i128,
                                    rust_name: "Incremental",
                                    sql_name: "Incremental",
                                }];
                let idx =
                    <<crate::sql_types::Integer as
                                diesel::sql_types::EnumSqlType<true, __DB>>::Strategy as
                                diesel::internal::derives::enum_::EnumMapping<__DB>>::map_from_database_value(value,
                            "AutoVacuumMode", VARIANTS)?;
                match idx {
                    0usize => Ok(Self::None),
                    1usize => Ok(Self::Full),
                    2usize => Ok(Self::Incremental),
                    _ => {
                        ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
                                format_args!("We construct all relevant variants")));
                    }
                }
            }
        }
        impl<__DB> diesel::serialize::ToSql<crate::sql_types::Integer, __DB>
            for AutoVacuumMode where __DB: diesel::backend::Backend,
            crate::sql_types::Integer: diesel::sql_types::EnumSqlType<true,
            __DB>,
            <crate::sql_types::Integer as
            diesel::sql_types::EnumSqlType<true,
            __DB>>::Strategy: diesel::internal::derives::enum_::EnumMapping<__DB>
            {
            fn to_sql<'b>(&'b self,
                output: &mut diesel::serialize::Output<'b, '_, __DB>)
                -> diesel::serialize::Result {
                let variant =
                    match self {
                        Self::None =>
                            &diesel::internal::derives::enum_::EnumVariant {
                                    discriminant: 0i128,
                                    rust_name: "None",
                                    sql_name: "None",
                                },
                        Self::Full =>
                            &diesel::internal::derives::enum_::EnumVariant {
                                    discriminant: 1i128,
                                    rust_name: "Full",
                                    sql_name: "Full",
                                },
                        Self::Incremental =>
                            &diesel::internal::derives::enum_::EnumVariant {
                                    discriminant: 2i128,
                                    rust_name: "Incremental",
                                    sql_name: "Incremental",
                                },
                    };
                <<crate::sql_types::Integer as
                        diesel::sql_types::EnumSqlType<true, __DB>>::Strategy as
                        diesel::internal::derives::enum_::EnumMapping<__DB>>::map_to_database_value(output,
                    variant)
            }
        }
        impl<'__expr>
            diesel::expression::AsExpression<crate::sql_types::Integer> for
            &'__expr AutoVacuumMode {
            type Expression =
                diesel::internal::derives::as_expression::Bound<crate::sql_types::Integer,
                Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<crate::sql_types::Integer>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        #[diagnostic::do_not_recommend]
        impl<'__expr>
            diesel::expression::AsExpression<diesel::sql_types::Nullable<crate::sql_types::Integer>>
            for &'__expr AutoVacuumMode {
            type Expression =
                diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<crate::sql_types::Integer>,
                Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<diesel::sql_types::Nullable<crate::sql_types::Integer>>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        #[diagnostic::do_not_recommend]
        impl<'__expr, '__expr2>
            diesel::expression::AsExpression<crate::sql_types::Integer> for
            &'__expr2 &'__expr AutoVacuumMode {
            type Expression =
                diesel::internal::derives::as_expression::Bound<crate::sql_types::Integer,
                Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<crate::sql_types::Integer>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        #[diagnostic::do_not_recommend]
        impl<'__expr, '__expr2>
            diesel::expression::AsExpression<diesel::sql_types::Nullable<crate::sql_types::Integer>>
            for &'__expr2 &'__expr AutoVacuumMode {
            type Expression =
                diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<crate::sql_types::Integer>,
                Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<diesel::sql_types::Nullable<crate::sql_types::Integer>>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        impl<__DB>
            diesel::serialize::ToSql<diesel::sql_types::Nullable<crate::sql_types::Integer>,
            __DB> for AutoVacuumMode where __DB: diesel::backend::Backend,
            Self: diesel::serialize::ToSql<crate::sql_types::Integer, __DB> {
            fn to_sql<'__b>(&'__b self,
                out: &mut diesel::serialize::Output<'__b, '_, __DB>)
                -> diesel::serialize::Result {
                diesel::serialize::ToSql::<crate::sql_types::Integer,
                        __DB>::to_sql(self, out)
            }
        }
        impl diesel::expression::AsExpression<crate::sql_types::Integer> for
            AutoVacuumMode {
            type Expression =
                diesel::internal::derives::as_expression::Bound<crate::sql_types::Integer,
                Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<crate::sql_types::Integer>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        impl diesel::expression::AsExpression<diesel::sql_types::Nullable<crate::sql_types::Integer>>
            for AutoVacuumMode {
            type Expression =
                diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<crate::sql_types::Integer>,
                Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<diesel::sql_types::Nullable<crate::sql_types::Integer>>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        impl<__DB, __ST> diesel::deserialize::Queryable<__ST, __DB> for
            AutoVacuumMode where __DB: diesel::backend::Backend,
            __ST: diesel::sql_types::SingleValue,
            Self: diesel::deserialize::FromSql<__ST, __DB> {
            type Row = Self;
            fn build(row: Self) -> diesel::deserialize::Result<Self> {
                diesel::deserialize::Result::Ok(row)
            }
        }
    };crate::types::Enum)]
395#[diesel(sql_type = crate::sql_types::Integer)]
396#[non_exhaustive]
397#[repr(i32)]
398pub enum AutoVacuumMode {
399    /// Freed pages stay on the freelist and the file never shrinks (default).
400    None = 0,
401    /// Freed pages are reclaimed and the file truncated at every commit.
402    Full = 1,
403    /// Freelist bookkeeping is kept, pages are reclaimed only when
404    /// `incremental_vacuum` runs.
405    Incremental = 2,
406}
407
408impl SqliteConnection {
409    /// Run a transaction with `BEGIN IMMEDIATE`
410    ///
411    /// This method will return an error if a transaction is already open.
412    ///
413    /// # Example
414    ///
415    /// ```rust
416    /// # include!("../../doctest_setup.rs");
417    /// #
418    /// # fn main() {
419    /// #     run_test().unwrap();
420    /// # }
421    /// #
422    /// # fn run_test() -> QueryResult<()> {
423    /// #     let mut conn = SqliteConnection::establish(":memory:").unwrap();
424    /// conn.immediate_transaction(|conn| {
425    ///     // Do stuff in a transaction
426    ///     Ok(())
427    /// })
428    /// # }
429    /// ```
430    pub fn immediate_transaction<T, E, F>(&mut self, f: F) -> Result<T, E>
431    where
432        F: FnOnce(&mut Self) -> Result<T, E>,
433        E: From<Error>,
434    {
435        self.transaction_sql(f, "BEGIN IMMEDIATE")
436    }
437
438    /// Run a transaction with `BEGIN EXCLUSIVE`
439    ///
440    /// This method will return an error if a transaction is already open.
441    ///
442    /// # Example
443    ///
444    /// ```rust
445    /// # include!("../../doctest_setup.rs");
446    /// #
447    /// # fn main() {
448    /// #     run_test().unwrap();
449    /// # }
450    /// #
451    /// # fn run_test() -> QueryResult<()> {
452    /// #     let mut conn = SqliteConnection::establish(":memory:").unwrap();
453    /// conn.exclusive_transaction(|conn| {
454    ///     // Do stuff in a transaction
455    ///     Ok(())
456    /// })
457    /// # }
458    /// ```
459    pub fn exclusive_transaction<T, E, F>(&mut self, f: F) -> Result<T, E>
460    where
461        F: FnOnce(&mut Self) -> Result<T, E>,
462        E: From<Error>,
463    {
464        self.transaction_sql(f, "BEGIN EXCLUSIVE")
465    }
466
467    /// Returns the rowid of the most recent successful INSERT on this connection.
468    ///
469    /// Returns `None` if no successful INSERT into a rowid table has been performed
470    /// on this connection, and `Some(rowid)` otherwise.
471    ///
472    /// See [the SQLite documentation](https://www.sqlite.org/c3ref/last_insert_rowid.html)
473    /// for details.
474    ///
475    /// # Caveats
476    /// - Inserts into `WITHOUT ROWID` tables are not recorded
477    /// - Failed `INSERT` (constraint violations) do not change the value
478    /// - `INSERT OR REPLACE` always updates the value
479    /// - Within triggers, returns the rowid of the trigger's INSERT;
480    ///   reverts after the trigger completes
481    ///
482    /// # Example
483    /// ```rust
484    /// # include!("../../doctest_setup.rs");
485    /// # fn main() {
486    /// #     run_test().unwrap();
487    /// # }
488    /// # fn run_test() -> QueryResult<()> {
489    /// use core::num::NonZeroI64;
490    /// use diesel::connection::SimpleConnection;
491    /// let conn = &mut SqliteConnection::establish(":memory:").unwrap();
492    /// conn.batch_execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)")?;
493    /// conn.batch_execute("INSERT INTO users (name) VALUES ('Sean')")?;
494    /// let rowid = conn.last_insert_rowid();
495    /// assert_eq!(rowid, NonZeroI64::new(1));
496    /// conn.batch_execute("INSERT INTO users (name) VALUES ('Tess')")?;
497    /// let rowid = conn.last_insert_rowid();
498    /// assert_eq!(rowid, NonZeroI64::new(2));
499    /// # Ok(())
500    /// # }
501    /// ```
502    pub fn last_insert_rowid(&self) -> Option<NonZeroI64> {
503        NonZeroI64::new(self.raw_connection.last_insert_rowid())
504    }
505
506    /// Returns an object that can be used to stream a BLOB from the database
507    ///
508    /// # Example
509    ///
510    /// ```rust
511    /// # include!("../../doctest_setup.rs");
512    /// # table! {
513    /// #     myblobs {
514    /// #         id -> Integer,
515    /// #         mydata -> Blob,
516    /// #     }
517    /// # }
518    /// # fn main() {
519    /// #     run_test().unwrap();
520    /// # }
521    /// # fn run_test() -> Result<(), Box<dyn std::error::Error>> {
522    /// use std::io::Read;
523    /// use diesel::connection::SimpleConnection;
524    /// let conn = &mut SqliteConnection::establish(":memory:").unwrap();
525    /// conn.batch_execute("CREATE TABLE myblobs (id INTEGER PRIMARY KEY, mydata BLOB)")?;
526    /// conn.batch_execute("INSERT INTO myblobs (mydata) VALUES ('abc')")?;
527    /// let mut data = conn.get_read_only_blob(myblobs::mydata, 1)?;
528    /// let mut buf = vec![];
529    /// data.read_to_end(&mut buf)?;
530    /// assert_eq!(buf, b"abc");
531    /// # Ok(())
532    /// # }
533    /// ```
534    pub fn get_read_only_blob<'conn, 'query, U>(
535        &'conn self,
536        blob_column: U,
537        row_id: i64,
538    ) -> Result<sqlite_blob::SqliteReadOnlyBlob<'conn>, Error>
539    where
540        'query: 'conn,
541        U: ColumnHasTable,
542        U::Table: NamedTable,
543    {
544        let table = blob_column.table();
545
546        let database_name = table.schema().unwrap_or("main");
547        let column_name = blob_column.name();
548        let table_name = table.table();
549
550        self.raw_connection
551            .blob_open(database_name, table_name, column_name, row_id)
552    }
553
554    fn transaction_sql<T, E, F>(&mut self, f: F, sql: &str) -> Result<T, E>
555    where
556        F: FnOnce(&mut Self) -> Result<T, E>,
557        E: From<Error>,
558    {
559        AnsiTransactionManager::begin_transaction_sql(&mut *self, sql)?;
560        match f(&mut *self) {
561            Ok(value) => {
562                AnsiTransactionManager::commit_transaction(&mut *self)?;
563                Ok(value)
564            }
565            Err(e) => {
566                AnsiTransactionManager::rollback_transaction(&mut *self)?;
567                Err(e)
568            }
569        }
570    }
571
572    fn prepared_query<'conn, 'query, T>(
573        &'conn mut self,
574        source: T,
575    ) -> QueryResult<StatementUse<'conn, 'query>>
576    where
577        T: QueryFragment<Sqlite> + QueryId + 'query,
578    {
579        self.instrumentation
580            .on_connection_event(InstrumentationEvent::StartQuery {
581                query: &crate::debug_query(&source),
582            });
583        let raw_connection = &self.raw_connection;
584        let cache = &mut self.statement_cache;
585        let statement = match cache.cached_statement(
586            &source,
587            &Sqlite,
588            &[],
589            raw_connection,
590            Statement::prepare,
591            &mut *self.instrumentation,
592        ) {
593            Ok(statement) => statement,
594            Err(e) => {
595                self.instrumentation
596                    .on_connection_event(InstrumentationEvent::FinishQuery {
597                        query: &crate::debug_query(&source),
598                        error: Some(&e),
599                    });
600
601                return Err(e);
602            }
603        };
604
605        StatementUse::bind(statement, source, &mut *self.instrumentation)
606    }
607
608    #[doc(hidden)]
609    pub fn register_sql_function<ArgsSqlType, RetSqlType, Args, Ret, F>(
610        &mut self,
611        fn_name: &str,
612        behavior: SqliteFunctionBehavior,
613        mut f: F,
614    ) -> QueryResult<()>
615    where
616        F: FnMut(Args) -> Ret + core::panic::UnwindSafe + Send + 'static,
617        Args: FromSqlRow<ArgsSqlType, Sqlite> + StaticallySizedRow<ArgsSqlType, Sqlite>,
618        Ret: ToSql<RetSqlType, Sqlite>,
619        Sqlite: HasSqlType<RetSqlType>,
620    {
621        functions::register(&self.raw_connection, fn_name, behavior, move |_, args| {
622            f(args)
623        })
624    }
625
626    #[doc(hidden)]
627    pub fn register_noarg_sql_function<RetSqlType, Ret, F>(
628        &mut self,
629        fn_name: &str,
630        behavior: SqliteFunctionBehavior,
631        f: F,
632    ) -> QueryResult<()>
633    where
634        F: FnMut() -> Ret + core::panic::UnwindSafe + Send + 'static,
635        Ret: ToSql<RetSqlType, Sqlite>,
636        Sqlite: HasSqlType<RetSqlType>,
637    {
638        functions::register_noargs(&self.raw_connection, fn_name, behavior, f)
639    }
640
641    #[doc(hidden)]
642    pub fn register_aggregate_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
643        &mut self,
644        fn_name: &str,
645        behavior: SqliteFunctionBehavior,
646    ) -> QueryResult<()>
647    where
648        A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send + core::panic::UnwindSafe,
649        Args: FromSqlRow<ArgsSqlType, Sqlite> + StaticallySizedRow<ArgsSqlType, Sqlite>,
650        Ret: ToSql<RetSqlType, Sqlite>,
651        Sqlite: HasSqlType<RetSqlType>,
652    {
653        functions::register_aggregate::<_, _, _, _, A>(&self.raw_connection, fn_name, behavior)
654    }
655
656    /// Register a collation function.
657    ///
658    /// `collation` must always return the same answer given the same inputs.
659    /// If `collation` panics and unwinds the stack, the process is aborted, since it is used
660    /// across a C FFI boundary, which cannot be unwound across and there is no way to
661    /// signal failures via the SQLite interface in this case..
662    ///
663    /// If the name is already registered it will be overwritten.
664    ///
665    /// This method will return an error if registering the function fails, either due to an
666    /// out-of-memory situation or because a collation with that name already exists and is
667    /// currently being used in parallel by a query.
668    ///
669    /// The collation needs to be specified when creating a table:
670    /// `CREATE TABLE my_table ( str TEXT COLLATE MY_COLLATION )`,
671    /// where `MY_COLLATION` corresponds to name passed as `collation_name`.
672    ///
673    /// # Example
674    ///
675    /// ```rust
676    /// # include!("../../doctest_setup.rs");
677    /// #
678    /// # fn main() {
679    /// #     run_test().unwrap();
680    /// # }
681    /// #
682    /// # fn run_test() -> QueryResult<()> {
683    /// #     let mut conn = SqliteConnection::establish(":memory:").unwrap();
684    /// // sqlite NOCASE only works for ASCII characters,
685    /// // this collation allows handling UTF-8 (barring locale differences)
686    /// conn.register_collation("RUSTNOCASE", |rhs, lhs| {
687    ///     rhs.to_lowercase().cmp(&lhs.to_lowercase())
688    /// })
689    /// # }
690    /// ```
691    pub fn register_collation<F>(&mut self, collation_name: &str, collation: F) -> QueryResult<()>
692    where
693        F: Fn(&str, &str) -> core::cmp::Ordering + Send + 'static + core::panic::UnwindSafe,
694    {
695        self.raw_connection
696            .register_collation_function(collation_name, collation)
697    }
698
699    /// Serialize the current SQLite database into a byte buffer.
700    ///
701    /// The serialized data is identical to the data that would be written to disk if the database
702    /// was saved in a file.
703    ///
704    /// # Returns
705    ///
706    /// This function returns a byte slice representing the serialized database.
707    pub fn serialize_database_to_buffer(&mut self) -> SerializedDatabase {
708        self.raw_connection.serialize()
709    }
710
711    /// Deserialize an SQLite database from a byte buffer.
712    ///
713    /// This function takes a byte slice and attempts to deserialize it into a SQLite database.
714    /// If successful, the database is loaded into the connection. If the deserialization fails,
715    /// an error is returned.
716    ///
717    /// The database is opened in READONLY mode.
718    ///
719    /// # Example
720    ///
721    /// ```no_run
722    /// # use diesel::sqlite::SerializedDatabase;
723    /// # use diesel::sqlite::SqliteConnection;
724    /// # use diesel::result::QueryResult;
725    /// # use diesel::sql_query;
726    /// # use diesel::Connection;
727    /// # use diesel::RunQueryDsl;
728    /// # fn main() {
729    /// let connection = &mut SqliteConnection::establish(":memory:").unwrap();
730    ///
731    /// sql_query("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)")
732    ///     .execute(connection).unwrap();
733    /// sql_query("INSERT INTO users (name, email) VALUES ('John Doe', 'john.doe@example.com'), ('Jane Doe', 'jane.doe@example.com')")
734    ///     .execute(connection).unwrap();
735    ///
736    /// // Serialize the database to a byte vector
737    /// let serialized_db: SerializedDatabase = connection.serialize_database_to_buffer();
738    ///
739    /// // Create a new in-memory SQLite database
740    /// let connection = &mut SqliteConnection::establish(":memory:").unwrap();
741    ///
742    /// // Deserialize the byte vector into the new database
743    /// connection.deserialize_readonly_database_from_buffer(serialized_db.as_slice()).unwrap();
744    /// #
745    /// # }
746    /// ```
747    // TODO: Diesel 3.0 This signature needs to change, we want to expose more options (schema name, readonly)
748    // and also ensure that this is not as unsafe as the current construct anymore. Maybe just accept a owned buffer or static pointer
749    // only instead? (So `Cow<'static, [u8]>`?)
750    #[allow(unsafe_code)]
751    pub fn deserialize_readonly_database_from_buffer(&mut self, data: &[u8]) -> QueryResult<()> {
752        // we copy the buffer here
753        // to make sure the underlying buffer lives as long as the connection
754        self.serialized_data.push(data.to_vec());
755        let last = self
756            .serialized_data
757            .last()
758            .expect("We literally pushed it above, so it's there");
759        unsafe {
760            // SAFETY: We store the buffer inside of the connection and we never touch it until
761            // we drop the connection
762            self.raw_connection.deserialize(last)
763        }
764    }
765
766    /// Provides temporary access to the raw SQLite database connection handle.
767    ///
768    /// This method provides a way to access the underlying `sqlite3` pointer,
769    /// enabling direct use of the SQLite C API for advanced features that
770    /// Diesel does not wrap, such as the [session extension](https://www.sqlite.org/sessionintro.html),
771    /// [hooks](https://www.sqlite.org/c3ref/update_hook.html), or other advanced APIs.
772    ///
773    /// # Why Diesel Doesn't Wrap These APIs
774    ///
775    /// Certain SQLite features, such as the session extension, are **optional** and only
776    /// available when SQLite is compiled with specific flags (e.g., `-DSQLITE_ENABLE_SESSION`
777    /// and `-DSQLITE_ENABLE_PREUPDATE_HOOK` for sessions). These compile-time options determine
778    /// whether the corresponding C API functions exist in the SQLite library's ABI.
779    ///
780    /// Because Diesel must work with any SQLite library at runtime—including system-provided
781    /// libraries that may lack these optional features—it **cannot safely provide wrappers**
782    /// for APIs that may or may not exist. Doing so would either:
783    ///
784    /// - Cause **linker errors** at compile time if the user's `libsqlite3-sys` wasn't compiled
785    ///   with the required flags, or
786    /// - Cause **undefined behavior** at runtime if Diesel called functions that don't exist
787    ///   in the linked library.
788    ///
789    /// While feature gates could theoretically solve this problem, Diesel already has an
790    /// extensive API surface with many existing feature combinations. Each new feature gate
791    /// adds a **combinatorial explosion** of test configurations that must be validated,
792    /// making the library increasingly difficult to maintain. Therefore, exposing the raw
793    /// connection is the preferred approach for niche SQLite features.
794    ///
795    /// By exposing the raw connection handle, Diesel allows users who **know** they have
796    /// access to a properly configured SQLite build to use these advanced features directly
797    /// through their own FFI bindings.
798    ///
799    /// # Safety
800    ///
801    /// This method is marked `unsafe` because improper use of the raw connection handle
802    /// can lead to undefined behavior. The caller must ensure that:
803    ///
804    /// - The connection handle is **not closed** during the callback.
805    /// - The connection handle is **not stored** beyond the callback's scope.
806    /// - Concurrent access rules are respected (SQLite connections are not thread-safe
807    ///   unless using serialized threading mode).
808    /// - **Transaction state is not modified** — do not execute `BEGIN`, `COMMIT`,
809    ///   `ROLLBACK`, or `SAVEPOINT` statements via the raw handle. Diesel's
810    ///   [`AnsiTransactionManager`] tracks transaction nesting internally, and
811    ///   bypassing it will cause Diesel's view of the transaction state to diverge
812    ///   from SQLite's actual state.
813    /// - **Diesel's prepared statements are not disturbed** — do not call
814    ///   `sqlite3_finalize()` or `sqlite3_reset()` on statements that belong to
815    ///   Diesel's `StatementCache`. Doing so will cause use-after-free or
816    ///   double-free when Diesel later accesses those statements.
817    ///
818    /// [`AnsiTransactionManager`]: crate::connection::AnsiTransactionManager
819    ///
820    /// # Example
821    ///
822    /// ```rust
823    /// use diesel::sqlite::SqliteConnection;
824    /// use diesel::Connection;
825    ///
826    /// let mut conn = SqliteConnection::establish(":memory:").unwrap();
827    ///
828    /// // SAFETY: We do not close or store the connection handle,
829    /// // and we do not modify Diesel-managed state (transactions, cached statements).
830    /// let is_valid = unsafe {
831    ///     conn.with_raw_connection(|raw_conn| {
832    ///         // The raw connection pointer can be passed to SQLite C API functions
833    ///         // from your own `libsqlite3-sys` (native) or `sqlite-wasm-rs` (WASM)
834    ///         // dependency — for example, `sqlite3_get_autocommit(raw_conn)` or
835    ///         // `sqlite3session_create(raw_conn, ...)`.
836    ///         !raw_conn.is_null()
837    ///     })
838    /// };
839    /// assert!(is_valid);
840    /// ```
841    ///
842    /// # Platform Notes
843    ///
844    /// This method works identically on both native and WASM targets. However,
845    /// you must depend on the appropriate FFI crate for your target:
846    ///
847    /// - **Native**: Add `libsqlite3-sys` as a dependency
848    /// - **WASM** (`wasm32-unknown-unknown`): Add `sqlite-wasm-rs` as a dependency
849    ///
850    /// Both crates expose a compatible `sqlite3` type that can be used with the
851    /// pointer returned by this method.
852    #[allow(unsafe_code)]
853    pub unsafe fn with_raw_connection<R, F>(&mut self, f: F) -> R
854    where
855        F: FnOnce(*mut ffi::sqlite3) -> R,
856    {
857        f(self.raw_connection.internal_connection.as_ptr())
858    }
859
860    /// Runs `f` with a borrowed `SqliteConnection` wrapping `db`, giving SQLite
861    /// callbacks the full connection API. Statements prepared during `f` are
862    /// finalized on return, but `db` is left open, since SQLite owns it.
863    ///
864    /// # Safety
865    ///
866    /// `db` must be a valid `sqlite3` handle that stays open for the duration
867    /// of the call.
868    #[allow(unsafe_code)]
869    pub(crate) unsafe fn with_borrowed_connection<R>(
870        db: core::ptr::NonNull<ffi::sqlite3>,
871        f: impl FnOnce(&mut SqliteConnection) -> R,
872    ) -> R {
873        // Tears the borrowed connection down on every exit path, including a
874        // panic unwinding out of `f`.
875        struct Borrowed(core::mem::ManuallyDrop<SqliteConnection>);
876
877        impl Drop for Borrowed {
878            fn drop(&mut self) {
879                // SAFETY: `self.0` is not touched again after this take.
880                let conn = unsafe { core::mem::ManuallyDrop::take(&mut self.0) };
881                let SqliteConnection {
882                    statement_cache,
883                    raw_connection,
884                    ..
885                } = conn;
886                // Finalize prepared statements, but do not run `RawConnection`'s
887                // `Drop`, which would close a handle we do not own.
888                drop(statement_cache);
889                core::mem::forget(raw_connection);
890            }
891        }
892
893        let mut conn = Borrowed(core::mem::ManuallyDrop::new(SqliteConnection {
894            statement_cache: StatementCache::new(),
895            raw_connection: RawConnection::from_ptr(db),
896            transaction_state: AnsiTransactionManager::default(),
897            metadata_lookup: (),
898            instrumentation: DynInstrumentation::default_instrumentation(),
899            serialized_data: Vec::new(),
900        }));
901
902        let result = f(&mut conn.0);
903
904        // The borrowed connection is discarded without committing or rolling
905        // back, so a transaction left open by `f` would leak onto the handle.
906        if true {
    if !#[allow(non_exhaustive_omitted_patterns)] match AnsiTransactionManager::transaction_manager_status_mut(&mut *conn.0).transaction_depth()
                {
                Ok(None) => true,
                _ => false,
            } {
        {
            ::core::panicking::panic_fmt(format_args!("callback must not leave an open transaction on the borrowed connection"));
        }
    };
};debug_assert!(
907            matches!(
908                AnsiTransactionManager::transaction_manager_status_mut(&mut *conn.0)
909                    .transaction_depth(),
910                Ok(None)
911            ),
912            "callback must not leave an open transaction on the borrowed connection"
913        );
914
915        result
916    }
917
918    /// Set a runtime limit for this connection, returning its previous value.
919    ///
920    /// Lowering these limits is a way to harden a connection against untrusted
921    /// SQL. See the [SQLite documentation](https://www.sqlite.org/c3ref/limit.html)
922    /// for the meaning of each [`SqliteLimit`].
923    ///
924    /// # Example
925    ///
926    /// ```rust
927    /// # include!("../../doctest_setup.rs");
928    /// # fn main() { run_test(); }
929    /// # fn run_test() {
930    /// use diesel::sqlite::SqliteLimit;
931    ///
932    /// let mut conn = SqliteConnection::establish(":memory:").unwrap();
933    ///
934    /// // Cap SQL statement length at 1 KiB, keeping the previous value.
935    /// let previous = conn.set_limit(SqliteLimit::SqlLength, 1024);
936    /// assert!(previous > 0);
937    /// assert_eq!(conn.get_limit(SqliteLimit::SqlLength), 1024);
938    /// # }
939    /// ```
940    pub fn set_limit(&mut self, limit: SqliteLimit, value: i32) -> i32 {
941        self.raw_connection.set_limit(limit, value)
942    }
943
944    /// Get the current value of a runtime limit for this connection.
945    ///
946    /// See the [SQLite documentation](https://www.sqlite.org/c3ref/limit.html)
947    /// for the meaning of each [`SqliteLimit`].
948    ///
949    /// # Example
950    ///
951    /// ```rust
952    /// # include!("../../doctest_setup.rs");
953    /// # fn main() { run_test(); }
954    /// # fn run_test() {
955    /// use diesel::sqlite::SqliteLimit;
956    ///
957    /// let conn = SqliteConnection::establish(":memory:").unwrap();
958    /// assert!(conn.get_limit(SqliteLimit::SqlLength) > 0);
959    /// # }
960    /// ```
961    pub fn get_limit(&self, limit: SqliteLimit) -> i32 {
962        self.raw_connection.get_limit(limit)
963    }
964
965    /// Apply SQLite's recommended limits for hardening against untrusted SQL.
966    ///
967    /// These are the values from the "Untrusted SQL Inputs" table of SQLite's
968    /// [security documentation](https://sqlite.org/security.html). They are
969    /// intentionally restrictive, so call [`set_limit`](Self::set_limit)
970    /// afterwards to relax any that are too aggressive for your application.
971    ///
972    /// | Limit | Value |
973    /// |-------|-------|
974    /// | `Length` | 1,000,000 |
975    /// | `SqlLength` | 100,000 |
976    /// | `ColumnCount` | 100 |
977    /// | `ExprDepth` | 10 |
978    /// | `CompoundSelect` | 3 |
979    /// | `VdbeOp` | 25,000 |
980    /// | `FunctionArg` | 8 |
981    /// | `Attached` | 0 |
982    /// | `LikePatternLength` | 50 |
983    /// | `VariableNumber` | 10 |
984    /// | `TriggerDepth` | 10 |
985    ///
986    /// The table's `PARSER_DEPTH` recommendation is omitted because it is a
987    /// compile-time only setting with no runtime `sqlite3_limit()` category.
988    /// `WorkerThreads` is left untouched (its default of 0 is already safe).
989    ///
990    /// # Example
991    ///
992    /// ```rust
993    /// # include!("../../doctest_setup.rs");
994    /// # fn main() { run_test(); }
995    /// # fn run_test() {
996    /// use diesel::sqlite::SqliteLimit;
997    ///
998    /// let mut conn = SqliteConnection::establish(":memory:").unwrap();
999    /// conn.set_recommended_security_limits();
1000    /// assert_eq!(conn.get_limit(SqliteLimit::SqlLength), 100_000);
1001    ///
1002    /// // Relax an individual limit that is too strict for this application.
1003    /// conn.set_limit(SqliteLimit::VariableNumber, 999);
1004    /// assert_eq!(conn.get_limit(SqliteLimit::VariableNumber), 999);
1005    /// # }
1006    /// ```
1007    pub fn set_recommended_security_limits(&mut self) {
1008        self.set_limit(SqliteLimit::Length, SqliteLimit::SAFE_LENGTH_LIMIT);
1009        self.set_limit(SqliteLimit::SqlLength, SqliteLimit::SAFE_SQL_LENGTH_LIMIT);
1010        self.set_limit(
1011            SqliteLimit::ColumnCount,
1012            SqliteLimit::SAFE_COLUMN_COUNT_LIMIT,
1013        );
1014        self.set_limit(SqliteLimit::ExprDepth, SqliteLimit::SAFE_EXPR_DEPTH_LIMIT);
1015        self.set_limit(
1016            SqliteLimit::CompoundSelect,
1017            SqliteLimit::SAFE_COMPOUND_SELECT_LIMIT,
1018        );
1019        self.set_limit(SqliteLimit::VdbeOp, SqliteLimit::SAFE_VDBE_OP_LIMIT);
1020        self.set_limit(
1021            SqliteLimit::FunctionArg,
1022            SqliteLimit::SAFE_FUNCTION_ARG_LIMIT,
1023        );
1024        self.set_limit(SqliteLimit::Attached, SqliteLimit::SAFE_ATTACHED_LIMIT);
1025        self.set_limit(
1026            SqliteLimit::LikePatternLength,
1027            SqliteLimit::SAFE_LIKE_PATTERN_LENGTH_LIMIT,
1028        );
1029        self.set_limit(
1030            SqliteLimit::VariableNumber,
1031            SqliteLimit::SAFE_VARIABLE_NUMBER_LIMIT,
1032        );
1033        self.set_limit(
1034            SqliteLimit::TriggerDepth,
1035            SqliteLimit::SAFE_TRIGGER_DEPTH_LIMIT,
1036        );
1037    }
1038
1039    /// Enable or disable SQLite defensive mode.
1040    ///
1041    /// When enabled, defensive mode prevents direct writes to shadow tables
1042    /// (FTS5, R-Tree, etc.), dangerous PRAGMAs like `writable_schema`,
1043    /// `sqlite3_deserialize()` from opening unsafe database images, and other
1044    /// potentially dangerous operations. Enable it for any connection that may
1045    /// process untrusted data. It is the single most important hardening flag.
1046    ///
1047    /// Requires SQLite 3.26.0 or later, otherwise returns an error.
1048    ///
1049    /// # Security Hardening Recipe
1050    ///
1051    /// ```rust
1052    /// # include!("../../doctest_setup.rs");
1053    /// # fn main() {
1054    /// #     let mut conn = SqliteConnection::establish(":memory:").unwrap();
1055    /// conn.set_defensive(true).unwrap();
1056    /// conn.set_trusted_schema(false).unwrap();
1057    /// conn.set_recommended_security_limits();
1058    /// # }
1059    /// ```
1060    ///
1061    /// Extension loading is off by default. Enable it only when needed via
1062    /// [`with_load_extension_enabled`][Self::with_load_extension_enabled]. See
1063    /// [`set_recommended_security_limits`][Self::set_recommended_security_limits]
1064    /// to harden the SQLite resource limits as well.
1065    pub fn set_defensive(&mut self, enabled: bool) -> QueryResult<()> {
1066        self.raw_connection
1067            .set_db_config_bool(ffi::SQLITE_DBCONFIG_DEFENSIVE, enabled)
1068    }
1069
1070    /// Check if defensive mode is enabled.
1071    ///
1072    /// See [`set_defensive`][Self::set_defensive] for details.
1073    pub fn is_defensive(&self) -> QueryResult<bool> {
1074        self.raw_connection
1075            .get_db_config_bool(ffi::SQLITE_DBCONFIG_DEFENSIVE)
1076    }
1077
1078    /// Enable or disable trusted schema mode.
1079    ///
1080    /// When disabled (untrusted), SQL functions called from schema objects
1081    /// (views, triggers, CHECK constraints, DEFAULT expressions, generated
1082    /// columns, expression indexes) are restricted to those marked
1083    /// [`INNOCUOUS`][crate::sqlite::SqliteFunctionBehavior::INNOCUOUS]. Disable
1084    /// it when opening database files from untrusted sources, and register your
1085    /// custom functions with appropriate
1086    /// [`SqliteFunctionBehavior`][crate::sqlite::SqliteFunctionBehavior] flags.
1087    ///
1088    /// Requires SQLite 3.31.0 or later, otherwise returns an error.
1089    pub fn set_trusted_schema(&mut self, trusted: bool) -> QueryResult<()> {
1090        self.raw_connection
1091            .set_db_config_bool(ffi::SQLITE_DBCONFIG_TRUSTED_SCHEMA, trusted)
1092    }
1093
1094    /// Check if trusted schema mode is enabled.
1095    ///
1096    /// See [`set_trusted_schema`][Self::set_trusted_schema] for details.
1097    pub fn is_trusted_schema(&self) -> QueryResult<bool> {
1098        self.raw_connection
1099            .get_db_config_bool(ffi::SQLITE_DBCONFIG_TRUSTED_SCHEMA)
1100    }
1101
1102    /// Runs the given closure with the `load_extension()` SQL function enabled,
1103    /// disabling it again afterwards.
1104    ///
1105    /// This controls the [`load_extension()`](https://www.sqlite.org/lang_corefunc.html#load_extension)
1106    /// **SQL function**, not the `sqlite3_load_extension()` C API (which Diesel
1107    /// does not expose). Extension loading is off by default, and scoping it to a
1108    /// closure keeps the window in which it is enabled as small as possible.
1109    ///
1110    /// Requires SQLite 3.13.0 or later, otherwise returns an error. Has no effect
1111    /// if SQLite was compiled with `SQLITE_OMIT_LOAD_EXTENSION`.
1112    ///
1113    /// # Panics
1114    ///
1115    /// If `f` panics, extension loading is disabled again before the panic
1116    /// resumes. no-std builds cannot catch the unwind, so there the flag is
1117    /// restored only on a normal return.
1118    ///
1119    /// # Example
1120    ///
1121    /// ```rust
1122    /// # include!("../../doctest_setup.rs");
1123    /// # fn main() {
1124    /// #     let mut conn = SqliteConnection::establish(":memory:").unwrap();
1125    /// let result: QueryResult<()> = conn.with_load_extension_enabled(|_conn| Ok(()));
1126    /// result.unwrap();
1127    /// # }
1128    /// ```
1129    pub fn with_load_extension_enabled<R, E>(
1130        &mut self,
1131        f: impl FnOnce(&mut Self) -> Result<R, E>,
1132    ) -> Result<R, E>
1133    where
1134        E: From<crate::result::Error>,
1135    {
1136        self.set_load_extension_enabled(true)?;
1137
1138        // On std builds, catch a panic from `f` so extension loading is restored
1139        // before the panic is resumed. no-std cannot catch unwinding, so there
1140        // the flag is restored only on a normal return.
1141        #[cfg(feature = "std")]
1142        {
1143            match std::panic::catch_unwind(core::panic::AssertUnwindSafe(|| f(self))) {
1144                Ok(r) => {
1145                    self.set_load_extension_enabled(false)?;
1146                    r
1147                }
1148                Err(panic) => {
1149                    let _ = self.set_load_extension_enabled(false);
1150                    std::panic::resume_unwind(panic);
1151                }
1152            }
1153        }
1154        #[cfg(not(feature = "std"))]
1155        {
1156            let r = f(self);
1157            self.set_load_extension_enabled(false)?;
1158            r
1159        }
1160    }
1161
1162    fn set_load_extension_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1163        self.raw_connection
1164            .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, enabled)
1165    }
1166
1167    #[cfg(test)]
1168    fn is_load_extension_enabled(&self) -> QueryResult<bool> {
1169        self.raw_connection
1170            .get_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION)
1171    }
1172
1173    /// Enable or disable the `fts3_tokenizer()` SQL function.
1174    ///
1175    /// The [`fts3_tokenizer()`](https://www.sqlite.org/fts3.html#f3tknzr) function
1176    /// allows overloading the default FTS3/FTS4 tokenizer, which can be exploited
1177    /// if an attacker can execute arbitrary SQL. Disable it unless you need custom
1178    /// FTS3 tokenizers.
1179    ///
1180    /// Requires SQLite 3.12.0 or later, otherwise returns an error.
1181    pub fn set_fts3_tokenizer_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1182        self.raw_connection
1183            .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, enabled)
1184    }
1185
1186    /// Check if the `fts3_tokenizer()` SQL function is enabled.
1187    ///
1188    /// See [`set_fts3_tokenizer_enabled`][Self::set_fts3_tokenizer_enabled] for details.
1189    pub fn is_fts3_tokenizer_enabled(&self) -> QueryResult<bool> {
1190        self.raw_connection
1191            .get_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER)
1192    }
1193
1194    /// Enable or disable direct writes to `sqlite_master`.
1195    ///
1196    /// When enabled, allows direct modification of the `sqlite_master` table,
1197    /// which can corrupt the database if misused. Keep it disabled unless you
1198    /// need to repair or modify the schema directly. Defensive mode
1199    /// ([`set_defensive`][Self::set_defensive]) also prevents this.
1200    ///
1201    /// Requires SQLite 3.28.0 or later, otherwise returns an error.
1202    pub fn set_writable_schema(&mut self, enabled: bool) -> QueryResult<()> {
1203        self.raw_connection
1204            .set_db_config_bool(ffi::SQLITE_DBCONFIG_WRITABLE_SCHEMA, enabled)
1205    }
1206
1207    /// Check if direct writes to `sqlite_master` are enabled.
1208    ///
1209    /// See [`set_writable_schema`][Self::set_writable_schema] for details.
1210    pub fn is_writable_schema(&self) -> QueryResult<bool> {
1211        self.raw_connection
1212            .get_db_config_bool(ffi::SQLITE_DBCONFIG_WRITABLE_SCHEMA)
1213    }
1214
1215    /// Enable or disable ATTACH from creating new database files.
1216    ///
1217    /// When disabled, [`ATTACH`](https://www.sqlite.org/lang_attach.html) can only
1218    /// open existing database files, not create new ones. Disable it where
1219    /// database file creation should be restricted.
1220    ///
1221    /// Requires SQLite 3.49.0 or later, otherwise returns an error.
1222    pub fn set_attach_create_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1223        self.raw_connection
1224            .set_db_config_bool(raw::SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE, enabled)
1225    }
1226
1227    /// Check if ATTACH can create new database files.
1228    ///
1229    /// See [`set_attach_create_enabled`][Self::set_attach_create_enabled] for details.
1230    pub fn is_attach_create_enabled(&self) -> QueryResult<bool> {
1231        self.raw_connection
1232            .get_db_config_bool(raw::SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE)
1233    }
1234
1235    /// Enable or disable ATTACH from opening databases in write mode.
1236    ///
1237    /// When disabled, all attached databases are opened as read-only. Disable it
1238    /// to restrict write access to attached databases.
1239    ///
1240    /// Requires SQLite 3.49.0 or later, otherwise returns an error.
1241    pub fn set_attach_write_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1242        self.raw_connection
1243            .set_db_config_bool(raw::SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE, enabled)
1244    }
1245
1246    /// Check if ATTACH can open databases in write mode.
1247    ///
1248    /// See [`set_attach_write_enabled`][Self::set_attach_write_enabled] for details.
1249    pub fn is_attach_write_enabled(&self) -> QueryResult<bool> {
1250        self.raw_connection
1251            .get_db_config_bool(raw::SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE)
1252    }
1253
1254    /// Attach the database file at `path` under `schema_name`.
1255    ///
1256    /// Runs [`ATTACH DATABASE ? AS ?`](https://www.sqlite.org/lang_attach.html) with
1257    /// both operands bound as parameters, so no SQL string escaping is needed.
1258    /// Diesel always opens connections with `SQLITE_OPEN_URI`, so a path beginning
1259    /// with `file:` is interpreted as a URI exactly as it is in
1260    /// [`SqliteConnection::establish`].
1261    ///
1262    /// A missing file is created as an empty database unless
1263    /// [`set_attach_create_enabled(false)`][Self::set_attach_create_enabled] is set,
1264    /// and [`set_attach_write_enabled(false)`][Self::set_attach_write_enabled] attaches
1265    /// read-only. The attach count is bounded (10 by default). A transaction across the
1266    /// main and an attached file is crash-atomic per file only under WAL.
1267    ///
1268    /// # Example
1269    ///
1270    /// ```rust
1271    /// # include!("../../doctest_setup.rs");
1272    /// #
1273    /// # fn main() {
1274    /// #     run_test().unwrap();
1275    /// # }
1276    /// #
1277    /// # fn run_test() -> QueryResult<()> {
1278    /// #     let conn = &mut SqliteConnection::establish(":memory:").unwrap();
1279    /// conn.attach_database(":memory:", "aux")?;
1280    /// conn.detach_database("aux")?;
1281    /// #     Ok(())
1282    /// # }
1283    /// ```
1284    pub fn attach_database(&mut self, path: &str, schema_name: &str) -> QueryResult<()> {
1285        use crate::query_dsl::RunQueryDsl;
1286        AttachDatabase { path, schema_name }
1287            .execute(self)
1288            .map(|_| ())
1289    }
1290
1291    /// Detach the database previously attached under `schema_name`.
1292    ///
1293    /// Runs [`DETACH DATABASE ?`](https://www.sqlite.org/lang_detach.html) with the
1294    /// schema name bound as a parameter. Detaching a schema still in use fails with an
1295    /// ordinary error.
1296    pub fn detach_database(&mut self, schema_name: &str) -> QueryResult<()> {
1297        use crate::query_dsl::RunQueryDsl;
1298        DetachDatabase { schema_name }.execute(self).map(|_| ())
1299    }
1300
1301    /// Enable or disable trigger execution.
1302    ///
1303    /// When disabled, triggers will not fire for any DML operations.
1304    ///
1305    /// Requires SQLite 3.8.7 or later, otherwise returns an error.
1306    pub fn set_triggers_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1307        self.raw_connection
1308            .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_TRIGGER, enabled)
1309    }
1310
1311    /// Check if trigger execution is enabled.
1312    ///
1313    /// See [`set_triggers_enabled`][Self::set_triggers_enabled] for details.
1314    pub fn are_triggers_enabled(&self) -> QueryResult<bool> {
1315        self.raw_connection
1316            .get_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_TRIGGER)
1317    }
1318
1319    /// Enable or disable view expansion.
1320    ///
1321    /// When disabled, queries against views will fail.
1322    ///
1323    /// Requires SQLite 3.30.0 or later, otherwise returns an error.
1324    pub fn set_views_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1325        self.raw_connection
1326            .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_VIEW, enabled)
1327    }
1328
1329    /// Check if view expansion is enabled.
1330    ///
1331    /// See [`set_views_enabled`][Self::set_views_enabled] for details.
1332    pub fn are_views_enabled(&self) -> QueryResult<bool> {
1333        self.raw_connection
1334            .get_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_VIEW)
1335    }
1336
1337    /// Enable or disable foreign key constraint enforcement.
1338    ///
1339    /// This is equivalent to `PRAGMA foreign_keys = ON/OFF`.
1340    ///
1341    /// Requires SQLite 3.8.7 or later, otherwise returns an error.
1342    pub fn set_foreign_keys_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1343        self.raw_connection
1344            .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_FKEY, enabled)
1345    }
1346
1347    /// Check if foreign key constraints are enabled.
1348    ///
1349    /// See [`set_foreign_keys_enabled`][Self::set_foreign_keys_enabled] for details.
1350    pub fn are_foreign_keys_enabled(&self) -> QueryResult<bool> {
1351        self.raw_connection
1352            .get_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_FKEY)
1353    }
1354
1355    /// Enable or disable double-quoted strings in DML statements.
1356    ///
1357    /// When enabled, double-quoted strings are interpreted as string literals
1358    /// rather than identifiers, a legacy behavior that can cause issues. Disable
1359    /// it for stricter SQL compliance.
1360    ///
1361    /// Requires SQLite 3.29.0 or later, otherwise returns an error.
1362    pub fn set_double_quoted_strings_dml(&mut self, enabled: bool) -> QueryResult<()> {
1363        self.raw_connection
1364            .set_db_config_bool(ffi::SQLITE_DBCONFIG_DQS_DML, enabled)
1365    }
1366
1367    /// Check if double-quoted strings in DML are enabled.
1368    ///
1369    /// See [`set_double_quoted_strings_dml`][Self::set_double_quoted_strings_dml] for details.
1370    pub fn are_double_quoted_strings_dml_enabled(&self) -> QueryResult<bool> {
1371        self.raw_connection
1372            .get_db_config_bool(ffi::SQLITE_DBCONFIG_DQS_DML)
1373    }
1374
1375    /// Enable or disable double-quoted strings in DDL statements.
1376    ///
1377    /// When enabled, double-quoted strings are interpreted as string literals
1378    /// rather than identifiers, a legacy behavior that can cause issues. Disable
1379    /// it for stricter SQL compliance.
1380    ///
1381    /// Requires SQLite 3.29.0 or later, otherwise returns an error.
1382    pub fn set_double_quoted_strings_ddl(&mut self, enabled: bool) -> QueryResult<()> {
1383        self.raw_connection
1384            .set_db_config_bool(ffi::SQLITE_DBCONFIG_DQS_DDL, enabled)
1385    }
1386
1387    /// Check if double-quoted strings in DDL are enabled.
1388    ///
1389    /// See [`set_double_quoted_strings_ddl`][Self::set_double_quoted_strings_ddl] for details.
1390    pub fn are_double_quoted_strings_ddl_enabled(&self) -> QueryResult<bool> {
1391        self.raw_connection
1392            .get_db_config_bool(ffi::SQLITE_DBCONFIG_DQS_DDL)
1393    }
1394
1395    /// Read the [`auto_vacuum`](AutoVacuumMode) mode of a database.
1396    ///
1397    /// `schema` selects an attached database by name, `None` reads `main`.
1398    ///
1399    /// ```rust
1400    /// # include!("../../doctest_setup.rs");
1401    /// # fn main() {
1402    /// #     run_test().unwrap();
1403    /// # }
1404    /// # fn run_test() -> QueryResult<()> {
1405    /// use diesel::sqlite::AutoVacuumMode;
1406    /// let conn = &mut SqliteConnection::establish(":memory:").unwrap();
1407    /// assert_eq!(conn.auto_vacuum(None)?, AutoVacuumMode::None);
1408    /// #     Ok(())
1409    /// # }
1410    /// ```
1411    pub fn auto_vacuum(&mut self, schema: Option<&str>) -> QueryResult<AutoVacuumMode> {
1412        use crate::query_dsl::RunQueryDsl;
1413        let query: Pragma<'_, crate::sql_types::Integer> = Pragma::new("auto_vacuum", schema);
1414        query.get_result(self)
1415    }
1416
1417    /// Set the [`auto_vacuum`](AutoVacuumMode) mode of a database.
1418    ///
1419    /// `schema` selects an attached database by name, `None` targets `main`.
1420    /// Changing from or to [`AutoVacuumMode::None`] only takes effect on a
1421    /// database with no tables yet, or after a subsequent `VACUUM`.
1422    ///
1423    /// ```rust
1424    /// # include!("../../doctest_setup.rs");
1425    /// # fn main() {
1426    /// #     run_test().unwrap();
1427    /// # }
1428    /// # fn run_test() -> QueryResult<()> {
1429    /// use diesel::sqlite::AutoVacuumMode;
1430    /// let conn = &mut SqliteConnection::establish(":memory:").unwrap();
1431    /// conn.set_auto_vacuum(None, AutoVacuumMode::Incremental)?;
1432    /// assert_eq!(conn.auto_vacuum(None)?, AutoVacuumMode::Incremental);
1433    /// #     Ok(())
1434    /// # }
1435    /// ```
1436    pub fn set_auto_vacuum(
1437        &mut self,
1438        schema: Option<&str>,
1439        mode: AutoVacuumMode,
1440    ) -> QueryResult<()> {
1441        use crate::query_dsl::RunQueryDsl;
1442        // #[repr(i32)] guarantees the discriminant fits exactly in i32.
1443        SetPragmaInt {
1444            schema,
1445            name: "auto_vacuum",
1446            value: mode as i32,
1447        }
1448        .execute(self)
1449        .map(|_| ())
1450    }
1451
1452    /// Total number of pages in a database, via `PRAGMA page_count`.
1453    ///
1454    /// `schema` selects an attached database by name, `None` reads `main`.
1455    /// Multiply by the page size for the size the database accounts for.
1456    ///
1457    /// ```rust
1458    /// # include!("../../doctest_setup.rs");
1459    /// # fn main() {
1460    /// #     run_test().unwrap();
1461    /// # }
1462    /// # fn run_test() -> QueryResult<()> {
1463    /// use diesel::connection::SimpleConnection;
1464    /// let conn = &mut SqliteConnection::establish(":memory:").unwrap();
1465    /// // An empty database occupies no pages until something is written.
1466    /// assert_eq!(conn.page_count(None)?, 0);
1467    /// conn.batch_execute("CREATE TABLE items (id INTEGER PRIMARY KEY)")?;
1468    /// assert!(conn.page_count(None)? > 0);
1469    /// #     Ok(())
1470    /// # }
1471    /// ```
1472    pub fn page_count(&mut self, schema: Option<&str>) -> QueryResult<i64> {
1473        self.read_pragma_count("page_count", schema)
1474    }
1475
1476    /// Unused pages on a database's freelist, via `PRAGMA freelist_count`.
1477    ///
1478    /// `schema` selects an attached database by name, `None` reads `main`. A
1479    /// growing freelist is reclaimable space, freed by `VACUUM`.
1480    ///
1481    /// ```rust
1482    /// # include!("../../doctest_setup.rs");
1483    /// # fn main() {
1484    /// #     run_test().unwrap();
1485    /// # }
1486    /// # fn run_test() -> QueryResult<()> {
1487    /// let conn = &mut SqliteConnection::establish(":memory:").unwrap();
1488    /// assert_eq!(conn.freelist_count(None)?, 0);
1489    /// #     Ok(())
1490    /// # }
1491    /// ```
1492    pub fn freelist_count(&mut self, schema: Option<&str>) -> QueryResult<i64> {
1493        self.read_pragma_count("freelist_count", schema)
1494    }
1495
1496    fn read_pragma_count(
1497        &mut self,
1498        pragma: &'static str,
1499        schema: Option<&str>,
1500    ) -> QueryResult<i64> {
1501        use crate::query_dsl::RunQueryDsl;
1502
1503        let query: Pragma<'_, crate::sql_types::BigInt> = Pragma::new(pragma, schema);
1504        query.get_result(self)
1505    }
1506
1507    /// Shrink a database by releasing freelist pages, without the full rewrite
1508    /// [`VACUUM`](https://www.sqlite.org/lang_vacuum.html) performs.
1509    ///
1510    /// `schema` selects an attached database by name, `None` targets `main`. `pages`
1511    /// bounds how many pages are reclaimed. As SQLite specifies, `None` or a value
1512    /// below one clears the whole freelist, as does a bound larger than it. Only
1513    /// databases in [`AutoVacuumMode::Incremental`] have anything to reclaim, on any
1514    /// other mode this succeeds and does nothing.
1515    ///
1516    /// ```rust
1517    /// # include!("../../doctest_setup.rs");
1518    /// # fn main() {
1519    /// #     run_test().unwrap();
1520    /// # }
1521    /// # fn run_test() -> QueryResult<()> {
1522    /// use diesel::sqlite::AutoVacuumMode;
1523    /// let conn = &mut SqliteConnection::establish(":memory:").unwrap();
1524    /// conn.set_auto_vacuum(None, AutoVacuumMode::Incremental)?;
1525    /// // Reclaim at most 8 pages, then whatever is left.
1526    /// conn.incremental_vacuum(None, Some(8))?;
1527    /// conn.incremental_vacuum(None, None)?;
1528    /// #     Ok(())
1529    /// # }
1530    /// ```
1531    pub fn incremental_vacuum(
1532        &mut self,
1533        schema: Option<&str>,
1534        pages: Option<u32>,
1535    ) -> QueryResult<()> {
1536        use crate::connection::SimpleConnection;
1537        use crate::query_builder::QueryBuilder;
1538        use crate::sqlite::SqliteQueryBuilder;
1539
1540        // SQLite frees one page per step of this statement, so it only empties the
1541        // freelist when run to completion. `batch_execute` uses `sqlite3_exec`, which
1542        // does that. A prepared statement would not: `StatementUse::run` steps once,
1543        // which frees a single page and silently leaves the rest.
1544        let mut query = SqliteQueryBuilder::new();
1545        query.push_sql("PRAGMA ");
1546        query.push_identifier(schema.unwrap_or("main"))?;
1547        query.push_sql(".incremental_vacuum");
1548        if let Some(pages) = pages {
1549            query.push_sql("(");
1550            query.push_sql(&pages.to_string());
1551            query.push_sql(")");
1552        }
1553        self.batch_execute(&query.finish())
1554    }
1555
1556    /// Rebuild a database, repacking it into the smallest space it can occupy.
1557    ///
1558    /// `schema` selects an attached database by name, `None` targets `main`.
1559    ///
1560    /// This cannot run inside a transaction, needs free space of up to twice the size
1561    /// of the database while it runs, and renumbers the implicit `rowid` of any table
1562    /// declared without an `INTEGER PRIMARY KEY`.
1563    ///
1564    /// Naming a schema requires SQLite 3.24.0 or later, otherwise returns an error.
1565    ///
1566    /// ```rust
1567    /// # include!("../../doctest_setup.rs");
1568    /// # fn main() {
1569    /// #     run_test().unwrap();
1570    /// # }
1571    /// # fn run_test() -> QueryResult<()> {
1572    /// let conn = &mut SqliteConnection::establish(":memory:").unwrap();
1573    /// conn.vacuum(None)?;
1574    /// #     Ok(())
1575    /// # }
1576    /// ```
1577    pub fn vacuum(&mut self, schema: Option<&str>) -> QueryResult<()> {
1578        use crate::query_dsl::RunQueryDsl;
1579
1580        Vacuum { schema, into: None }.execute(self).map(|_| ())
1581    }
1582
1583    /// Write a vacuumed copy of a database to `path`, leaving the original untouched.
1584    ///
1585    /// This is SQLite's online backup: the copy is consistent, defragmented, and taken
1586    /// without blocking readers. `schema` selects an attached database by name, `None`
1587    /// copies `main`. The path is a bind parameter, so it needs no quoting.
1588    ///
1589    /// `path` may name a file that does not exist or one that is empty, but writing
1590    /// over an existing database fails rather than replacing it.
1591    ///
1592    /// Requires SQLite 3.27.0 or later, otherwise returns an error.
1593    ///
1594    /// ```rust
1595    /// # include!("../../doctest_setup.rs");
1596    /// # fn main() {
1597    /// #     run_test().unwrap();
1598    /// # }
1599    /// # fn run_test() -> QueryResult<()> {
1600    /// # let dir = tempfile::tempdir().unwrap();
1601    /// # let backup = dir.path().join("backup.db");
1602    /// let conn = &mut SqliteConnection::establish(":memory:").unwrap();
1603    /// conn.vacuum_into(None, backup.to_str().unwrap())?;
1604    /// # assert!(backup.exists());
1605    /// #     Ok(())
1606    /// # }
1607    /// ```
1608    pub fn vacuum_into(&mut self, schema: Option<&str>, path: &str) -> QueryResult<()> {
1609        use crate::query_dsl::RunQueryDsl;
1610
1611        Vacuum {
1612            schema,
1613            into: Some(path),
1614        }
1615        .execute(self)
1616        .map(|_| ())
1617    }
1618
1619    fn register_diesel_sql_functions(&self) -> QueryResult<()> {
1620        use crate::sql_types::{Integer, Text};
1621
1622        // This function has side effects (creates triggers), so it should not
1623        // be deterministic. We use DIRECTONLY to prevent it from being called
1624        // from malicious schema objects in untrusted databases.
1625        functions::register::<Text, Integer, _, _, _>(
1626            &self.raw_connection,
1627            "diesel_manage_updated_at",
1628            SqliteFunctionBehavior::DIRECTONLY,
1629            |conn, table_name: String| {
1630                conn.exec(&::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("CREATE TRIGGER __diesel_manage_updated_at_{0}\nAFTER UPDATE ON {0}\nFOR EACH ROW WHEN\n  old.updated_at IS NULL AND\n  new.updated_at IS NULL OR\n  old.updated_at == new.updated_at\nBEGIN\n  UPDATE {0}\n  SET updated_at = CURRENT_TIMESTAMP\n  WHERE ROWID = new.ROWID;\nEND\n",
                table_name))
    })alloc::format!(
1631                    include_str!("diesel_manage_updated_at.sql"),
1632                    table_name = table_name
1633                ))
1634                .expect("Failed to create trigger");
1635                0 // have to return *something*
1636            },
1637        )
1638    }
1639
1640    fn establish_inner(database_url: &str) -> Result<SqliteConnection, ConnectionError> {
1641        use crate::result::ConnectionError::CouldntSetupConfiguration;
1642        let raw_connection = RawConnection::establish(database_url)?;
1643        let conn = Self {
1644            statement_cache: StatementCache::new(),
1645            raw_connection,
1646            transaction_state: AnsiTransactionManager::default(),
1647            metadata_lookup: (),
1648            instrumentation: DynInstrumentation::none(),
1649            serialized_data: Vec::new(),
1650        };
1651        conn.register_diesel_sql_functions()
1652            .map_err(CouldntSetupConfiguration)?;
1653        Ok(conn)
1654    }
1655}
1656
1657fn error_message(err_code: libc::c_int) -> &'static str {
1658    ffi::code_to_str(err_code)
1659}
1660
1661#[derive(const _: () =
    {
        use diesel;
        #[allow(non_camel_case_types)]
        impl<'a> diesel::query_builder::QueryId for AttachDatabase<'a> {
            type QueryId = AttachDatabase<'static>;
            const HAS_STATIC_QUERY_ID: bool = true;
            const IS_WINDOW_FUNCTION: bool = false;
        }
    };QueryId)]
1662struct AttachDatabase<'a> {
1663    path: &'a str,
1664    schema_name: &'a str,
1665}
1666
1667impl QueryFragment<Sqlite> for AttachDatabase<'_> {
1668    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> {
1669        out.push_sql("ATTACH DATABASE ");
1670        out.push_bind_param::<crate::sql_types::Text, _>(self.path)?;
1671        out.push_sql(" AS ");
1672        out.push_bind_param::<crate::sql_types::Text, _>(self.schema_name)?;
1673        Ok(())
1674    }
1675}
1676
1677impl RunQueryDslSupport for AttachDatabase<'_> {}
1678
1679#[derive(const _: () =
    {
        use diesel;
        #[allow(non_camel_case_types)]
        impl<'a> diesel::query_builder::QueryId for DetachDatabase<'a> {
            type QueryId = DetachDatabase<'static>;
            const HAS_STATIC_QUERY_ID: bool = true;
            const IS_WINDOW_FUNCTION: bool = false;
        }
    };QueryId)]
1680struct DetachDatabase<'a> {
1681    schema_name: &'a str,
1682}
1683
1684impl QueryFragment<Sqlite> for DetachDatabase<'_> {
1685    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> {
1686        out.push_sql("DETACH DATABASE ");
1687        out.push_bind_param::<crate::sql_types::Text, _>(self.schema_name)?;
1688        Ok(())
1689    }
1690}
1691
1692impl RunQueryDslSupport for DetachDatabase<'_> {}
1693
1694// A `PRAGMA` accepts no bind parameters, neither for the schema it targets nor for the
1695// value it assigns, so the schema is rendered as a quoted identifier by the query
1696// builder. `name` is always a constant chosen here, never caller data.
1697struct Pragma<'a, ST> {
1698    schema: Option<&'a str>,
1699    name: &'static str,
1700    sql_type: PhantomData<ST>,
1701}
1702
1703impl<'a, ST> Pragma<'a, ST> {
1704    fn new(name: &'static str, schema: Option<&'a str>) -> Self {
1705        Pragma {
1706            schema,
1707            name,
1708            sql_type: PhantomData,
1709        }
1710    }
1711}
1712
1713impl<ST> QueryFragment<Sqlite> for Pragma<'_, ST> {
1714    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> {
1715        out.push_sql("PRAGMA ");
1716        out.push_identifier(self.schema.unwrap_or("main"))?;
1717        out.push_sql(".");
1718        out.push_sql(self.name);
1719        Ok(())
1720    }
1721}
1722
1723// The schema name is runtime data, so the rendered SQL is not determined by the type.
1724impl<ST> QueryId for Pragma<'_, ST> {
1725    type QueryId = ();
1726
1727    const HAS_STATIC_QUERY_ID: bool = false;
1728}
1729
1730impl<ST> Query for Pragma<'_, ST> {
1731    type SqlType = ST;
1732}
1733
1734impl<ST> RunQueryDslSupport for Pragma<'_, ST> {}
1735
1736// `PRAGMA name = value` takes no bind parameter for the value either, so the integer is
1737// rendered as a literal.
1738struct SetPragmaInt<'a> {
1739    schema: Option<&'a str>,
1740    name: &'static str,
1741    value: i32,
1742}
1743
1744impl QueryFragment<Sqlite> for SetPragmaInt<'_> {
1745    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> {
1746        out.push_sql("PRAGMA ");
1747        out.push_identifier(self.schema.unwrap_or("main"))?;
1748        out.push_sql(".");
1749        out.push_sql(self.name);
1750        out.push_sql(" = ");
1751        out.push_sql(&self.value.to_string());
1752        Ok(())
1753    }
1754}
1755
1756impl QueryId for SetPragmaInt<'_> {
1757    type QueryId = ();
1758
1759    const HAS_STATIC_QUERY_ID: bool = false;
1760}
1761
1762impl RunQueryDslSupport for SetPragmaInt<'_> {}
1763
1764// `VACUUM` names its schema as an identifier, so that operand is quoted by the query
1765// builder, while the `INTO` destination is an expression and binds normally.
1766struct Vacuum<'a> {
1767    schema: Option<&'a str>,
1768    into: Option<&'a str>,
1769}
1770
1771impl QueryFragment<Sqlite> for Vacuum<'_> {
1772    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> {
1773        out.push_sql("VACUUM ");
1774        out.push_identifier(self.schema.unwrap_or("main"))?;
1775        if let Some(into) = self.into {
1776            out.push_sql(" INTO ");
1777            out.push_bind_param::<crate::sql_types::Text, _>(into)?;
1778        }
1779        Ok(())
1780    }
1781}
1782
1783// The schema name is runtime data, so the rendered SQL is not determined by the type.
1784impl QueryId for Vacuum<'_> {
1785    type QueryId = ();
1786
1787    const HAS_STATIC_QUERY_ID: bool = false;
1788}
1789
1790impl RunQueryDslSupport for Vacuum<'_> {}
1791
1792#[cfg(test)]
1793mod tests {
1794    use super::*;
1795    use crate::dsl::sql;
1796    use crate::prelude::*;
1797    use crate::sql_types::{Integer, Text};
1798    use crate::sqlite::SqliteFunctionBehavior;
1799
1800    fn connection() -> SqliteConnection {
1801        SqliteConnection::establish(":memory:").unwrap()
1802    }
1803
1804    #[diesel_test_helper::test]
1805    #[allow(unsafe_code)]
1806    fn with_raw_connection_can_return_values() {
1807        let connection = &mut connection();
1808
1809        // SAFETY: We only read connection status, which doesn't modify state.
1810        let autocommit_status = unsafe {
1811            connection.with_raw_connection(|raw_conn| ffi::sqlite3_get_autocommit(raw_conn))
1812        };
1813
1814        // Outside a transaction, autocommit should be enabled (returns non-zero)
1815        assert_ne!(autocommit_status, 0, "Expected autocommit to be enabled");
1816    }
1817
1818    #[diesel_test_helper::test]
1819    #[allow(unsafe_code)]
1820    fn with_raw_connection_works_after_diesel_operations() {
1821        let connection = &mut connection();
1822
1823        // First, do some Diesel operations
1824        crate::sql_query("CREATE TABLE test_table (id INTEGER PRIMARY KEY, value TEXT)")
1825            .execute(connection)
1826            .unwrap();
1827        crate::sql_query("INSERT INTO test_table (value) VALUES ('hello')")
1828            .execute(connection)
1829            .unwrap();
1830
1831        // SAFETY: We only read the last insert rowid, which is a read-only operation.
1832        let last_rowid = unsafe {
1833            connection.with_raw_connection(|raw_conn| ffi::sqlite3_last_insert_rowid(raw_conn))
1834        };
1835
1836        assert_eq!(last_rowid, 1, "Last insert rowid should be 1");
1837
1838        // Verify Diesel still works after using raw connection
1839        let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM test_table")
1840            .get_result(connection)
1841            .unwrap();
1842        assert_eq!(count, 1);
1843    }
1844
1845    #[diesel_test_helper::test]
1846    #[allow(unsafe_code)]
1847    fn with_raw_connection_can_execute_raw_sql() {
1848        let connection = &mut connection();
1849
1850        // Create a table using Diesel first
1851        crate::sql_query("CREATE TABLE raw_test (id INTEGER PRIMARY KEY, name TEXT)")
1852            .execute(connection)
1853            .unwrap();
1854
1855        // SAFETY: We execute a simple INSERT via raw SQLite API.
1856        // This modifies the database but in a way compatible with Diesel.
1857        let result = unsafe {
1858            connection.with_raw_connection(|raw_conn| {
1859                let sql = c"INSERT INTO raw_test (name) VALUES ('from_raw')";
1860                let mut err_msg: *mut libc::c_char = core::ptr::null_mut();
1861                let rc = ffi::sqlite3_exec(
1862                    raw_conn,
1863                    sql.as_ptr(),
1864                    None,
1865                    core::ptr::null_mut(),
1866                    &mut err_msg,
1867                );
1868                if rc != ffi::SQLITE_OK && !err_msg.is_null() {
1869                    ffi::sqlite3_free(err_msg as *mut libc::c_void);
1870                }
1871                rc
1872            })
1873        };
1874
1875        assert_eq!(result, ffi::SQLITE_OK, "Raw SQL execution should succeed");
1876
1877        // Verify the insert worked using Diesel
1878        let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM raw_test")
1879            .get_result(connection)
1880            .unwrap();
1881        assert_eq!(count, 1);
1882
1883        let name: String = sql::<Text>("SELECT name FROM raw_test WHERE id = 1")
1884            .get_result(connection)
1885            .unwrap();
1886        assert_eq!(name, "from_raw");
1887    }
1888
1889    #[diesel_test_helper::test]
1890    #[allow(unsafe_code)]
1891    fn with_raw_connection_works_within_transaction() {
1892        let connection = &mut connection();
1893
1894        crate::sql_query("CREATE TABLE txn_test (id INTEGER PRIMARY KEY, value INTEGER)")
1895            .execute(connection)
1896            .unwrap();
1897
1898        connection
1899            .transaction::<_, crate::result::Error, _>(|conn| {
1900                crate::sql_query("INSERT INTO txn_test (value) VALUES (42)")
1901                    .execute(conn)
1902                    .unwrap();
1903
1904                // SAFETY: We only read the autocommit status inside a transaction.
1905                let autocommit = unsafe {
1906                    conn.with_raw_connection(|raw_conn| ffi::sqlite3_get_autocommit(raw_conn))
1907                };
1908
1909                // Inside a transaction, autocommit should be disabled (returns 0)
1910                assert_eq!(
1911                    autocommit, 0,
1912                    "Autocommit should be disabled inside transaction"
1913                );
1914
1915                Ok(())
1916            })
1917            .unwrap();
1918
1919        // After transaction commits, autocommit should be re-enabled
1920        let autocommit = unsafe {
1921            connection.with_raw_connection(|raw_conn| ffi::sqlite3_get_autocommit(raw_conn))
1922        };
1923        assert_ne!(
1924            autocommit, 0,
1925            "Autocommit should be enabled after transaction"
1926        );
1927    }
1928
1929    #[diesel_test_helper::test]
1930    #[allow(unsafe_code)]
1931    fn with_raw_connection_can_read_database_filename() {
1932        let connection = &mut connection();
1933
1934        // SAFETY: We only read the database filename, which is a read-only operation.
1935        let filename = unsafe {
1936            connection.with_raw_connection(|raw_conn| {
1937                let db_name = c"main";
1938                let filename_ptr = ffi::sqlite3_db_filename(raw_conn, db_name.as_ptr());
1939                if filename_ptr.is_null() {
1940                    None
1941                } else {
1942                    // For :memory: databases, this might return empty string or special value
1943                    let cstr = core::ffi::CStr::from_ptr(filename_ptr);
1944                    Some(cstr.to_string_lossy().into_owned())
1945                }
1946            })
1947        };
1948
1949        // For in-memory databases, sqlite3_db_filename returns a non-null pointer
1950        // to an empty string
1951        assert_eq!(
1952            filename,
1953            Some(String::new()),
1954            "In-memory database filename should be an empty string"
1955        );
1956    }
1957
1958    #[diesel_test_helper::test]
1959    #[allow(unsafe_code)]
1960    fn with_raw_connection_changes_count() {
1961        let connection = &mut connection();
1962
1963        crate::sql_query("CREATE TABLE changes_test (id INTEGER PRIMARY KEY, value INTEGER)")
1964            .execute(connection)
1965            .unwrap();
1966
1967        crate::sql_query("INSERT INTO changes_test (value) VALUES (1), (2), (3)")
1968            .execute(connection)
1969            .unwrap();
1970
1971        // Update all rows using raw connection
1972        let changes = unsafe {
1973            connection.with_raw_connection(|raw_conn| {
1974                let sql = c"UPDATE changes_test SET value = value + 10";
1975                let mut err_msg: *mut libc::c_char = core::ptr::null_mut();
1976                let rc = ffi::sqlite3_exec(
1977                    raw_conn,
1978                    sql.as_ptr(),
1979                    None,
1980                    core::ptr::null_mut(),
1981                    &mut err_msg,
1982                );
1983                if rc != ffi::SQLITE_OK && !err_msg.is_null() {
1984                    ffi::sqlite3_free(err_msg as *mut libc::c_void);
1985                    return -1;
1986                }
1987                ffi::sqlite3_changes(raw_conn)
1988            })
1989        };
1990
1991        assert_eq!(changes, 3, "Should have updated 3 rows");
1992
1993        // Verify the updates using Diesel
1994        let values: Vec<i32> = sql::<Integer>("SELECT value FROM changes_test ORDER BY id")
1995            .load(connection)
1996            .unwrap();
1997        assert_eq!(values, vec![11, 12, 13]);
1998    }
1999
2000    // catch_unwind is not available in WASM (panic = "abort")
2001    #[diesel_test_helper::test]
2002    #[allow(unsafe_code)]
2003    #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
2004    fn with_raw_connection_recovers_after_panic() {
2005        let connection = &mut connection();
2006
2007        crate::sql_query("CREATE TABLE panic_test (id INTEGER PRIMARY KEY, value TEXT)")
2008            .execute(connection)
2009            .unwrap();
2010
2011        // Panic inside the callback
2012        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
2013            connection.with_raw_connection(|_raw_conn| {
2014                panic!("intentional panic inside with_raw_connection");
2015            })
2016        }));
2017        assert!(result.is_err(), "Should have caught the panic");
2018
2019        // Connection should still be usable after the panic
2020        crate::sql_query("INSERT INTO panic_test (value) VALUES ('after_panic')")
2021            .execute(connection)
2022            .unwrap();
2023
2024        let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM panic_test")
2025            .get_result(connection)
2026            .unwrap();
2027        assert_eq!(count, 1, "Connection should work after panic in callback");
2028    }
2029
2030    // Filesystem access is not available in WASM
2031    #[diesel_test_helper::test]
2032    #[allow(unsafe_code)]
2033    #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
2034    fn with_raw_connection_can_read_file_database_filename() {
2035        let dir = std::env::temp_dir().join("diesel_test_filename.db");
2036        let db_path = dir.to_str().unwrap();
2037
2038        // Clean up from any previous run
2039        let _ = std::fs::remove_file(db_path);
2040
2041        let connection = &mut SqliteConnection::establish(db_path).unwrap();
2042
2043        // SAFETY: We only read the database filename, which is a read-only operation.
2044        let filename = unsafe {
2045            connection.with_raw_connection(|raw_conn| {
2046                let db_name = c"main";
2047                let filename_ptr = ffi::sqlite3_db_filename(raw_conn, db_name.as_ptr());
2048                if filename_ptr.is_null() {
2049                    None
2050                } else {
2051                    let cstr = core::ffi::CStr::from_ptr(filename_ptr);
2052                    Some(cstr.to_string_lossy().into_owned())
2053                }
2054            })
2055        };
2056
2057        let filename = filename.expect("File-based database should have a filename");
2058        assert!(
2059            filename.contains("diesel_test_filename.db"),
2060            "Filename should contain the database name, got: {filename}"
2061        );
2062
2063        // Clean up
2064        let _ = std::fs::remove_file(db_path);
2065    }
2066
2067    #[declare_sql_function]
2068    extern "SQL" {
2069        fn fun_case(x: Text) -> Text;
2070        fn my_add(x: Integer, y: Integer) -> Integer;
2071        fn answer() -> Integer;
2072        fn add_counter(x: Integer) -> Integer;
2073
2074        #[aggregate]
2075        fn my_sum(expr: Integer) -> Integer;
2076        #[aggregate]
2077        fn range_max(expr1: Integer, expr2: Integer, expr3: Integer) -> Nullable<Integer>;
2078    }
2079
2080    #[diesel_test_helper::test]
2081    fn database_serializes_and_deserializes_successfully() {
2082        let expected_users = vec![
2083            (
2084                1,
2085                "John Doe".to_string(),
2086                "john.doe@example.com".to_string(),
2087            ),
2088            (
2089                2,
2090                "Jane Doe".to_string(),
2091                "jane.doe@example.com".to_string(),
2092            ),
2093        ];
2094
2095        let conn1 = &mut connection();
2096        let _ =
2097            crate::sql_query("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)")
2098                .execute(conn1);
2099        let _ = crate::sql_query("INSERT INTO users (name, email) VALUES ('John Doe', 'john.doe@example.com'), ('Jane Doe', 'jane.doe@example.com')")
2100            .execute(conn1);
2101
2102        for _i in 0..2 {
2103            let serialized_database = conn1.serialize_database_to_buffer();
2104            let conn2 = &mut connection();
2105            conn2
2106                .deserialize_readonly_database_from_buffer(serialized_database.as_slice())
2107                .unwrap();
2108
2109            let query =
2110                sql::<(Integer, Text, Text)>("SELECT id, name, email FROM users ORDER BY id");
2111            let actual_users = query.load::<(i32, String, String)>(conn2).unwrap();
2112
2113            assert_eq!(expected_users, actual_users);
2114            // drop the database here
2115            // and requery the database to make sure the database owns
2116            // required data
2117            std::mem::drop(serialized_database);
2118            let query =
2119                sql::<(Integer, Text, Text)>("SELECT id, name, email FROM users ORDER BY id");
2120            let actual_users = query.load::<(i32, String, String)>(conn2).unwrap();
2121
2122            assert_eq!(expected_users, actual_users);
2123        }
2124    }
2125
2126    #[diesel_test_helper::test]
2127    fn database_deserialize_random_bytes() {
2128        let buffer = vec![0, 1, 2, 3, 4];
2129        let conn = &mut SqliteConnection::establish(":memory:").unwrap();
2130
2131        conn.deserialize_readonly_database_from_buffer(&buffer)
2132            .unwrap();
2133
2134        let r = sql::<Integer>("SELECT id FROM users").load::<i32>(conn);
2135
2136        assert!(r.is_err());
2137        assert_eq!(r.unwrap_err().to_string(), "file is not a database");
2138
2139        let conn = &mut SqliteConnection::establish(":memory:").unwrap();
2140
2141        let _ =
2142            crate::sql_query("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)")
2143                .execute(conn);
2144        let _ = crate::sql_query("INSERT INTO users (name, email) VALUES ('John Doe', 'john.doe@example.com'), ('Jane Doe', 'jane.doe@example.com')")
2145            .execute(conn);
2146
2147        let db = conn.serialize_database_to_buffer();
2148        // only get a valid header, but append garbage
2149        let mut bad_buffer = db[..100].to_vec();
2150        bad_buffer.extend(b"whatever");
2151        conn.deserialize_readonly_database_from_buffer(&bad_buffer)
2152            .unwrap();
2153
2154        let r = sql::<Integer>("SELECT id FROM users").load::<i32>(conn);
2155
2156        assert!(r.is_err());
2157        assert_eq!(
2158            r.unwrap_err().to_string(),
2159            "database disk image is malformed"
2160        );
2161
2162        // only get a valid header, but append garbage
2163        let mut size_fitting_bad_buffer = db[..100].to_vec();
2164        size_fitting_bad_buffer.extend(
2165            core::iter::repeat(b"abcdefghij")
2166                .flatten()
2167                .take(db.len() - 100),
2168        );
2169        let r = conn.deserialize_readonly_database_from_buffer(&size_fitting_bad_buffer);
2170
2171        assert!(r.is_err());
2172        assert_eq!(
2173            r.unwrap_err().to_string(),
2174            "database disk image is malformed"
2175        );
2176    }
2177
2178    #[diesel_test_helper::test]
2179    fn register_custom_function() {
2180        let connection = &mut connection();
2181        fun_case_utils::register_impl(connection, |x: String| {
2182            x.chars()
2183                .enumerate()
2184                .map(|(i, c)| {
2185                    if i % 2 == 0 {
2186                        c.to_lowercase().to_string()
2187                    } else {
2188                        c.to_uppercase().to_string()
2189                    }
2190                })
2191                .collect::<String>()
2192        })
2193        .unwrap();
2194
2195        let mapped_string = crate::select(fun_case("foobar"))
2196            .get_result::<String>(connection)
2197            .unwrap();
2198        assert_eq!("fOoBaR", mapped_string);
2199    }
2200
2201    #[diesel_test_helper::test]
2202    fn register_multiarg_function() {
2203        let connection = &mut connection();
2204        my_add_utils::register_impl(connection, |x: i32, y: i32| x + y).unwrap();
2205
2206        let added = crate::select(my_add(1, 2)).get_result::<i32>(connection);
2207        assert_eq!(Ok(3), added);
2208    }
2209
2210    #[diesel_test_helper::test]
2211    fn register_noarg_function() {
2212        let connection = &mut connection();
2213        answer_utils::register_impl(connection, || 42).unwrap();
2214
2215        let answer = crate::select(answer()).get_result::<i32>(connection);
2216        assert_eq!(Ok(42), answer);
2217    }
2218
2219    #[diesel_test_helper::test]
2220    fn register_nondeterministic_noarg_function() {
2221        let connection = &mut connection();
2222        answer_utils::register_nondeterministic_impl(connection, || 42).unwrap();
2223
2224        let answer = crate::select(answer()).get_result::<i32>(connection);
2225        assert_eq!(Ok(42), answer);
2226    }
2227
2228    #[diesel_test_helper::test]
2229    fn register_nondeterministic_function() {
2230        let connection = &mut connection();
2231        let mut y = 0;
2232        add_counter_utils::register_nondeterministic_impl(connection, move |x: i32| {
2233            y += 1;
2234            x + y
2235        })
2236        .unwrap();
2237
2238        let added = crate::select((add_counter(1), add_counter(1), add_counter(1)))
2239            .get_result::<(i32, i32, i32)>(connection);
2240        assert_eq!(Ok((2, 3, 4)), added);
2241    }
2242
2243    #[derive(Default)]
2244    struct MySum {
2245        sum: i32,
2246    }
2247
2248    impl SqliteAggregateFunction<i32> for MySum {
2249        type Output = i32;
2250
2251        fn step(&mut self, expr: i32) {
2252            self.sum += expr;
2253        }
2254
2255        fn finalize(aggregator: Option<Self>) -> Self::Output {
2256            aggregator.map(|a| a.sum).unwrap_or_default()
2257        }
2258    }
2259
2260    table! {
2261        my_sum_example {
2262            id -> Integer,
2263            value -> Integer,
2264        }
2265    }
2266
2267    #[diesel_test_helper::test]
2268    fn register_aggregate_function() {
2269        use self::my_sum_example::dsl::*;
2270
2271        let connection = &mut connection();
2272        crate::sql_query(
2273            "CREATE TABLE my_sum_example (id integer primary key autoincrement, value integer)",
2274        )
2275        .execute(connection)
2276        .unwrap();
2277        crate::sql_query("INSERT INTO my_sum_example (value) VALUES (1), (2), (3)")
2278            .execute(connection)
2279            .unwrap();
2280
2281        my_sum_utils::register_impl_with_behavior::<MySum, _>(
2282            connection,
2283            SqliteFunctionBehavior::DETERMINISTIC,
2284        )
2285        .unwrap();
2286
2287        let result = my_sum_example
2288            .select(my_sum(value))
2289            .get_result::<i32>(connection);
2290        assert_eq!(Ok(6), result);
2291    }
2292
2293    #[diesel_test_helper::test]
2294    fn register_aggregate_function_returns_finalize_default_on_empty_set() {
2295        use self::my_sum_example::dsl::*;
2296
2297        let connection = &mut connection();
2298        crate::sql_query(
2299            "CREATE TABLE my_sum_example (id integer primary key autoincrement, value integer)",
2300        )
2301        .execute(connection)
2302        .unwrap();
2303
2304        my_sum_utils::register_impl_with_behavior::<MySum, _>(
2305            connection,
2306            SqliteFunctionBehavior::DETERMINISTIC,
2307        )
2308        .unwrap();
2309
2310        let result = my_sum_example
2311            .select(my_sum(value))
2312            .get_result::<i32>(connection);
2313        assert_eq!(Ok(0), result);
2314    }
2315
2316    #[derive(Default)]
2317    struct RangeMax<T> {
2318        max_value: Option<T>,
2319    }
2320
2321    impl<T: Default + Ord + Copy + Clone> SqliteAggregateFunction<(T, T, T)> for RangeMax<T> {
2322        type Output = Option<T>;
2323
2324        fn step(&mut self, (x0, x1, x2): (T, T, T)) {
2325            let max = if x0 >= x1 && x0 >= x2 {
2326                x0
2327            } else if x1 >= x0 && x1 >= x2 {
2328                x1
2329            } else {
2330                x2
2331            };
2332
2333            self.max_value = match self.max_value {
2334                Some(current_max_value) if max > current_max_value => Some(max),
2335                None => Some(max),
2336                _ => self.max_value,
2337            };
2338        }
2339
2340        fn finalize(aggregator: Option<Self>) -> Self::Output {
2341            aggregator?.max_value
2342        }
2343    }
2344
2345    table! {
2346        range_max_example {
2347            id -> Integer,
2348            value1 -> Integer,
2349            value2 -> Integer,
2350            value3 -> Integer,
2351        }
2352    }
2353
2354    #[diesel_test_helper::test]
2355    fn register_aggregate_multiarg_function() {
2356        use self::range_max_example::dsl::*;
2357
2358        let connection = &mut connection();
2359        crate::sql_query(
2360            r#"CREATE TABLE range_max_example (
2361                id integer primary key autoincrement,
2362                value1 integer,
2363                value2 integer,
2364                value3 integer
2365            )"#,
2366        )
2367        .execute(connection)
2368        .unwrap();
2369        crate::sql_query(
2370            "INSERT INTO range_max_example (value1, value2, value3) VALUES (3, 2, 1), (2, 2, 2)",
2371        )
2372        .execute(connection)
2373        .unwrap();
2374
2375        range_max_utils::register_impl_with_behavior::<RangeMax<i32>, _, _, _>(
2376            connection,
2377            SqliteFunctionBehavior::DETERMINISTIC,
2378        )
2379        .unwrap();
2380        let result = range_max_example
2381            .select(range_max(value1, value2, value3))
2382            .get_result::<Option<i32>>(connection)
2383            .unwrap();
2384        assert_eq!(Some(3), result);
2385    }
2386
2387    table! {
2388        my_collation_example {
2389            id -> Integer,
2390            value -> Text,
2391        }
2392    }
2393
2394    #[diesel_test_helper::test]
2395    fn register_collation_function() {
2396        use self::my_collation_example::dsl::*;
2397
2398        let connection = &mut connection();
2399
2400        connection
2401            .register_collation("RUSTNOCASE", |rhs, lhs| {
2402                rhs.to_lowercase().cmp(&lhs.to_lowercase())
2403            })
2404            .unwrap();
2405
2406        crate::sql_query(
2407                "CREATE TABLE my_collation_example (id integer primary key autoincrement, value text collate RUSTNOCASE)",
2408            ).execute(connection)
2409            .unwrap();
2410        crate::sql_query(
2411            "INSERT INTO my_collation_example (value) VALUES ('foo'), ('FOo'), ('f00')",
2412        )
2413        .execute(connection)
2414        .unwrap();
2415
2416        let result = my_collation_example
2417            .filter(value.eq("foo"))
2418            .select(value)
2419            .load::<String>(connection);
2420        assert_eq!(
2421            Ok(&["foo".to_owned(), "FOo".to_owned()][..]),
2422            result.as_ref().map(|vec| vec.as_ref())
2423        );
2424
2425        let result = my_collation_example
2426            .filter(value.eq("FOO"))
2427            .select(value)
2428            .load::<String>(connection);
2429        assert_eq!(
2430            Ok(&["foo".to_owned(), "FOo".to_owned()][..]),
2431            result.as_ref().map(|vec| vec.as_ref())
2432        );
2433
2434        let result = my_collation_example
2435            .filter(value.eq("f00"))
2436            .select(value)
2437            .load::<String>(connection);
2438        assert_eq!(
2439            Ok(&["f00".to_owned()][..]),
2440            result.as_ref().map(|vec| vec.as_ref())
2441        );
2442
2443        let result = my_collation_example
2444            .filter(value.eq("F00"))
2445            .select(value)
2446            .load::<String>(connection);
2447        assert_eq!(
2448            Ok(&["f00".to_owned()][..]),
2449            result.as_ref().map(|vec| vec.as_ref())
2450        );
2451
2452        let result = my_collation_example
2453            .filter(value.eq("oof"))
2454            .select(value)
2455            .load::<String>(connection);
2456        assert_eq!(Ok(&[][..]), result.as_ref().map(|vec| vec.as_ref()));
2457    }
2458
2459    // regression test for https://github.com/diesel-rs/diesel/issues/3425
2460    #[diesel_test_helper::test]
2461    fn test_correct_serialization_of_owned_strings() {
2462        use crate::prelude::*;
2463
2464        #[derive(Debug, crate::expression::AsExpression)]
2465        #[diesel(sql_type = diesel::sql_types::Text)]
2466        struct CustomWrapper(String);
2467
2468        impl crate::serialize::ToSql<Text, Sqlite> for CustomWrapper {
2469            fn to_sql<'b>(
2470                &'b self,
2471                out: &mut crate::serialize::Output<'b, '_, Sqlite>,
2472            ) -> crate::serialize::Result {
2473                out.set_value(self.0.to_string());
2474                Ok(crate::serialize::IsNull::No)
2475            }
2476        }
2477
2478        let connection = &mut connection();
2479
2480        let res = crate::select(
2481            CustomWrapper("".into())
2482                .into_sql::<crate::sql_types::Text>()
2483                .nullable(),
2484        )
2485        .get_result::<Option<String>>(connection)
2486        .unwrap();
2487        assert_eq!(res, Some(String::new()));
2488    }
2489
2490    #[diesel_test_helper::test]
2491    fn test_correct_serialization_of_owned_bytes() {
2492        use crate::prelude::*;
2493
2494        #[derive(Debug, crate::expression::AsExpression)]
2495        #[diesel(sql_type = diesel::sql_types::Binary)]
2496        struct CustomWrapper(Vec<u8>);
2497
2498        impl crate::serialize::ToSql<crate::sql_types::Binary, Sqlite> for CustomWrapper {
2499            fn to_sql<'b>(
2500                &'b self,
2501                out: &mut crate::serialize::Output<'b, '_, Sqlite>,
2502            ) -> crate::serialize::Result {
2503                out.set_value(self.0.clone());
2504                Ok(crate::serialize::IsNull::No)
2505            }
2506        }
2507
2508        let connection = &mut connection();
2509
2510        let res = crate::select(
2511            CustomWrapper(Vec::new())
2512                .into_sql::<crate::sql_types::Binary>()
2513                .nullable(),
2514        )
2515        .get_result::<Option<Vec<u8>>>(connection)
2516        .unwrap();
2517        assert_eq!(res, Some(Vec::new()));
2518    }
2519
2520    #[diesel_test_helper::test]
2521    fn correctly_handle_empty_query() {
2522        let check_empty_query_error = |r: crate::QueryResult<usize>| {
2523            assert!(r.is_err());
2524            let err = r.unwrap_err();
2525            assert!(
2526                matches!(err, crate::result::Error::QueryBuilderError(ref b) if b.is::<crate::result::EmptyQuery>()),
2527                "Expected a query builder error, but got {err}"
2528            );
2529        };
2530        let connection = &mut SqliteConnection::establish(":memory:").unwrap();
2531        check_empty_query_error(crate::sql_query("").execute(connection));
2532        check_empty_query_error(crate::sql_query("   ").execute(connection));
2533        check_empty_query_error(crate::sql_query("\n\t").execute(connection));
2534        check_empty_query_error(crate::sql_query("-- SELECT 1;").execute(connection));
2535    }
2536
2537    #[diesel_test_helper::test]
2538    fn last_insert_rowid_returns_none_on_fresh_connection() {
2539        let conn = &mut connection();
2540        assert_eq!(conn.last_insert_rowid(), None);
2541    }
2542
2543    #[diesel_test_helper::test]
2544    fn last_insert_rowid_returns_rowid_after_insert() {
2545        let conn = &mut connection();
2546        crate::sql_query("CREATE TABLE li_test (id INTEGER PRIMARY KEY, val TEXT NOT NULL)")
2547            .execute(conn)
2548            .unwrap();
2549
2550        crate::sql_query("INSERT INTO li_test (val) VALUES ('a')")
2551            .execute(conn)
2552            .unwrap();
2553        assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
2554
2555        crate::sql_query("INSERT INTO li_test (val) VALUES ('b')")
2556            .execute(conn)
2557            .unwrap();
2558        assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(2));
2559    }
2560
2561    #[diesel_test_helper::test]
2562    fn last_insert_rowid_unchanged_after_failed_insert() {
2563        let conn = &mut connection();
2564        crate::sql_query(
2565            "CREATE TABLE li_test2 (id INTEGER PRIMARY KEY, val TEXT NOT NULL UNIQUE)",
2566        )
2567        .execute(conn)
2568        .unwrap();
2569
2570        crate::sql_query("INSERT INTO li_test2 (val) VALUES ('a')")
2571            .execute(conn)
2572            .unwrap();
2573        let rowid = conn.last_insert_rowid();
2574        assert_eq!(rowid, NonZeroI64::new(1));
2575
2576        // This should fail due to UNIQUE constraint
2577        let result = crate::sql_query("INSERT INTO li_test2 (val) VALUES ('a')").execute(conn);
2578        assert!(result.is_err());
2579
2580        // rowid should be unchanged
2581        assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
2582    }
2583
2584    #[diesel_test_helper::test]
2585    fn last_insert_rowid_with_explicit_rowid() {
2586        let conn = &mut connection();
2587        crate::sql_query("CREATE TABLE li_test3 (id INTEGER PRIMARY KEY, val TEXT NOT NULL)")
2588            .execute(conn)
2589            .unwrap();
2590
2591        crate::sql_query("INSERT INTO li_test3 (id, val) VALUES (42, 'a')")
2592            .execute(conn)
2593            .unwrap();
2594        assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(42));
2595    }
2596
2597    #[diesel_test_helper::test]
2598    fn last_insert_rowid_unchanged_after_delete_and_update() {
2599        let conn = &mut connection();
2600        crate::sql_query("CREATE TABLE li_test4 (id INTEGER PRIMARY KEY, val TEXT NOT NULL)")
2601            .execute(conn)
2602            .unwrap();
2603
2604        crate::sql_query("INSERT INTO li_test4 (val) VALUES ('a')")
2605            .execute(conn)
2606            .unwrap();
2607        let rowid = conn.last_insert_rowid();
2608        assert_eq!(rowid, NonZeroI64::new(1));
2609
2610        crate::sql_query("UPDATE li_test4 SET val = 'b' WHERE id = 1")
2611            .execute(conn)
2612            .unwrap();
2613        assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
2614
2615        crate::sql_query("DELETE FROM li_test4 WHERE id = 1")
2616            .execute(conn)
2617            .unwrap();
2618        assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
2619    }
2620
2621    #[diesel_test_helper::test]
2622    fn read_bytes_from_blob() {
2623        table! {
2624            blobs {
2625                id -> Integer,
2626                data -> Blob,
2627                data2 -> Blob,
2628            }
2629        }
2630
2631        use std::io::Read;
2632
2633        let conn = &mut connection();
2634
2635        let _ =
2636            crate::sql_query("CREATE TABLE blobs (id INTEGER PRIMARY KEY, data BLOB, data2 BLOB)")
2637                .execute(conn);
2638
2639        let _ = crate::sql_query(
2640            "INSERT INTO blobs (data, data2) VALUES ('abc', 'def'), ('123', '456')",
2641        )
2642        .execute(conn);
2643
2644        let mut data = conn.get_read_only_blob(blobs::data, 1).unwrap();
2645        let mut buf = vec![];
2646        data.read_to_end(&mut buf).unwrap();
2647
2648        assert_eq!(buf, b"abc");
2649
2650        let mut data2 = conn.get_read_only_blob(blobs::data2, 1).unwrap();
2651        let mut buf = vec![];
2652        data2.read_to_end(&mut buf).unwrap();
2653
2654        assert_eq!(buf, b"def");
2655    }
2656
2657    #[diesel_test_helper::test]
2658    fn read_seek_bytes() {
2659        table! {
2660            blobs {
2661                id -> Integer,
2662                data -> Blob,
2663            }
2664        }
2665
2666        use std::io::Read;
2667        use std::io::Seek;
2668        use std::io::SeekFrom;
2669
2670        let conn = &mut connection();
2671
2672        let _ = crate::sql_query("CREATE TABLE blobs (id INTEGER PRIMARY KEY, data BLOB)")
2673            .execute(conn);
2674
2675        let _ = crate::sql_query("INSERT INTO blobs (data) VALUES ('abcdefghi')").execute(conn);
2676
2677        let mut data = conn.get_read_only_blob(blobs::data, 1).unwrap();
2678
2679        let mut buf = [0; 1];
2680        assert_eq!(data.read(&mut buf).unwrap(), 1);
2681        assert_eq!(&buf, b"a");
2682
2683        // Seek one forward
2684        assert_eq!(data.seek(SeekFrom::Current(1)).unwrap(), 2);
2685
2686        let mut buf = [0; 1];
2687        assert_eq!(data.read(&mut buf).unwrap(), 1);
2688        assert_eq!(&buf, b"c");
2689
2690        // Seek back to start
2691        assert_eq!(data.seek(SeekFrom::Start(0)).unwrap(), 0);
2692
2693        let mut buf = [0; 1];
2694        assert_eq!(data.read(&mut buf).unwrap(), 1);
2695        assert_eq!(&buf, b"a");
2696
2697        // Seek before start
2698        assert_eq!(data.seek(SeekFrom::Current(-10)).unwrap(), 0);
2699
2700        let mut buf = [0; 1];
2701        assert_eq!(data.read(&mut buf).unwrap(), 1);
2702        assert_eq!(&buf, b"a");
2703
2704        // Seek after end
2705        data.seek(SeekFrom::Current(100)).unwrap();
2706
2707        // Now we don't get any bytes back
2708        let mut buf = [0; 1];
2709        assert_eq!(data.read(&mut buf).unwrap(), 0);
2710    }
2711
2712    #[diesel_test_helper::test]
2713    fn use_conn_after_blob_drop() {
2714        table! {
2715            blobs {
2716                id -> Integer,
2717                data -> Blob,
2718            }
2719        }
2720
2721        let conn = &mut connection();
2722
2723        let _ = crate::sql_query("CREATE TABLE blobs (id INTEGER PRIMARY KEY, data BLOB)")
2724            .execute(conn);
2725
2726        let _ = crate::sql_query("INSERT INTO blobs (data) VALUES ('abc')").execute(conn);
2727
2728        let data = conn.get_read_only_blob(blobs::data, 1).unwrap();
2729        drop(data);
2730
2731        let _ = crate::sql_query("INSERT INTO blobs (data) VALUES ('def')").execute(conn);
2732    }
2733
2734    #[diesel_test_helper::test]
2735    fn blob_transaction() {
2736        table! {
2737            blobs {
2738                id -> Integer,
2739                data -> Blob,
2740            }
2741        }
2742
2743        use std::io::Read;
2744
2745        let conn = &mut connection();
2746
2747        let _ = crate::sql_query("CREATE TABLE blobs (id INTEGER PRIMARY KEY, data BLOB)")
2748            .execute(conn);
2749
2750        let _ = crate::sql_query("INSERT INTO blobs (data) VALUES ('abc')").execute(conn);
2751
2752        {
2753            let mut data = conn.get_read_only_blob(blobs::data, 1).unwrap();
2754            let mut buf = vec![];
2755            data.read_to_end(&mut buf).unwrap();
2756            assert_eq!(buf, b"abc");
2757        }
2758
2759        let res = conn.exclusive_transaction(|conn| {
2760            crate::sql_query("UPDATE blobs SET data = 'def' WHERE id = 1").execute(conn)?;
2761
2762            let mut data = conn.get_read_only_blob(blobs::data, 1).unwrap();
2763            let mut buf = vec![];
2764            data.read_to_end(&mut buf).unwrap();
2765            assert_eq!(buf, b"def");
2766
2767            Result::<(), _>::Err(Error::RollbackTransaction)
2768        });
2769
2770        assert_eq!(res.unwrap_err(), Error::RollbackTransaction);
2771
2772        let mut data = conn.get_read_only_blob(blobs::data, 1).unwrap();
2773        let mut buf = vec![];
2774        data.read_to_end(&mut buf).unwrap();
2775        assert_eq!(buf, b"abc");
2776    }
2777
2778    #[diesel_test_helper::test]
2779    fn aggregate_function_works_with_aligned_data() {
2780        #[derive(Debug, Default)]
2781        #[repr(align(64))]
2782        struct OverAligned;
2783
2784        impl SqliteAggregateFunction<i32> for OverAligned {
2785            type Output = i64;
2786
2787            fn step(&mut self, _value: i32) {
2788                let need = core::mem::align_of::<Self>();
2789                let got = core::mem::align_of_val(self);
2790                assert_eq!(need, got);
2791            }
2792
2793            fn finalize(_agg: Option<Self>) -> i64 {
2794                0
2795            }
2796        }
2797        #[declare_sql_function]
2798        extern "SQL" {
2799            #[aggregate]
2800            fn over_aligned_sum(x: Integer) -> diesel::sql_types::BigInt;
2801        }
2802
2803        let mut conn = SqliteConnection::establish(":memory:").unwrap();
2804        over_aligned_sum_utils::register_impl::<OverAligned, _>(&mut conn).unwrap();
2805
2806        diesel::select(over_aligned_sum(1))
2807            .execute(&mut conn)
2808            .unwrap();
2809    }
2810
2811    #[diesel_test_helper::test]
2812    fn sum_twice() {
2813        #[derive(Default)]
2814        struct Sum(i32);
2815
2816        impl SqliteAggregateFunction<i32> for Sum {
2817            type Output = i32;
2818
2819            fn step(&mut self, value: i32) {
2820                self.0 += value;
2821            }
2822
2823            fn finalize(agg: Option<Self>) -> i32 {
2824                agg.map(|s| s.0).unwrap_or_default()
2825            }
2826        }
2827
2828        #[declare_sql_function]
2829        extern "SQL" {
2830            #[aggregate]
2831            fn my_sum(x: Integer) -> Integer;
2832        }
2833
2834        let mut conn = SqliteConnection::establish(":memory:").unwrap();
2835        my_sum_utils::register_impl::<Sum, _>(&mut conn).unwrap();
2836
2837        conn.batch_execute(
2838            "
2839            CREATE TABLE test(key1 INTEGER, key2 INTEGER);
2840            INSERT INTO test(key1, key2) VALUES (1, 2), (2, 4), (3, 6);
2841",
2842        )
2843        .unwrap();
2844
2845        table! {
2846            test (key1, key2) {
2847                key1 -> Integer,
2848                key2 -> Integer,
2849            }
2850        }
2851
2852        let (first_res, second_res) = test::table
2853            .select((my_sum(test::key1), my_sum(test::key2)))
2854            .get_result::<(i32, i32)>(&mut conn)
2855            .unwrap();
2856
2857        assert_eq!(first_res, 6);
2858        assert_eq!(second_res, 12);
2859
2860        conn.batch_execute("DELETE FROM test").unwrap();
2861        let (first_res, second_res) = test::table
2862            .select((my_sum(test::key1), my_sum(test::key2)))
2863            .get_result::<(i32, i32)>(&mut conn)
2864            .unwrap();
2865
2866        assert_eq!(first_res, 0);
2867        assert_eq!(second_res, 0);
2868    }
2869
2870    #[diesel_test_helper::test]
2871    fn test_injection() {
2872        diesel::table! {
2873            #[sql_name = "quote'table"]
2874            quote_table (id) {
2875                id -> Nullable<Integer>,
2876                name -> Nullable<Text>,
2877            }
2878        }
2879
2880        let mut conn = SqliteConnection::establish(":memory:").unwrap();
2881
2882        conn.batch_execute("CREATE TABLE \"quote'table\" (id INTEGER PRIMARY KEY, name TEXT);")
2883            .unwrap();
2884
2885        diesel::insert_into(quote_table::table)
2886            .values((quote_table::id.eq(1), quote_table::name.eq("Jane")))
2887            .execute(&mut conn)
2888            .unwrap();
2889
2890        let data = quote_table::table
2891            .load::<(Option<i32>, Option<String>)>(&mut conn)
2892            .unwrap();
2893        assert_eq!(data, [(Some(1), Some("Jane".to_owned()))]);
2894    }
2895
2896    #[diesel_test_helper::test]
2897    fn set_limit_returns_previous_value() {
2898        let mut conn = connection();
2899        let original = conn.get_limit(SqliteLimit::SqlLength);
2900
2901        // Setting a new value returns the old one, and a second set returns the
2902        // value installed by the first.
2903        assert_eq!(conn.set_limit(SqliteLimit::SqlLength, 1024), original);
2904        assert_eq!(conn.set_limit(SqliteLimit::SqlLength, 2048), 1024);
2905        assert_eq!(conn.get_limit(SqliteLimit::SqlLength), 2048);
2906    }
2907
2908    #[diesel_test_helper::test]
2909    fn get_limit_does_not_mutate() {
2910        let conn = connection();
2911        let first = conn.get_limit(SqliteLimit::ExprDepth);
2912        // Querying is implemented by passing -1 to sqlite3_limit, which must
2913        // leave the limit unchanged.
2914        assert!(first > 0);
2915        assert_eq!(conn.get_limit(SqliteLimit::ExprDepth), first);
2916    }
2917
2918    #[diesel_test_helper::test]
2919    fn set_limit_enforces_length() {
2920        let mut conn = connection();
2921        conn.set_limit(SqliteLimit::Length, 100);
2922
2923        assert!(
2924            crate::sql_query("SELECT length(randomblob(50))")
2925                .execute(&mut conn)
2926                .is_ok()
2927        );
2928        // A 500-byte blob exceeds the 100-byte row/value limit ("string or blob too big").
2929        assert!(
2930            crate::sql_query("SELECT length(randomblob(500))")
2931                .execute(&mut conn)
2932                .is_err()
2933        );
2934    }
2935
2936    #[diesel_test_helper::test]
2937    fn set_limit_enforces_column_count() {
2938        // A wide result set runs under the default column limit but fails once the limit is
2939        // lowered below its column count ("too many columns in result set").
2940        let wide = format!(
2941            "SELECT {}",
2942            (1..=30)
2943                .map(|i| i.to_string())
2944                .collect::<Vec<_>>()
2945                .join(", ")
2946        );
2947
2948        let mut unconstrained = connection();
2949        assert!(crate::sql_query(&wide).execute(&mut unconstrained).is_ok());
2950
2951        let mut conn = connection();
2952        conn.set_limit(SqliteLimit::ColumnCount, 10);
2953        assert!(crate::sql_query(&wide).execute(&mut conn).is_err());
2954    }
2955
2956    #[diesel_test_helper::test]
2957    fn set_limit_enforces_expr_depth() {
2958        let mut conn = connection();
2959        conn.set_limit(SqliteLimit::ExprDepth, 5);
2960
2961        assert!(crate::sql_query("SELECT 1+1").execute(&mut conn).is_ok());
2962        // A 40-deep addition tree exceeds the parse-tree depth of five.
2963        let deep = format!("SELECT {}1", "1+".repeat(40));
2964        assert!(crate::sql_query(&deep).execute(&mut conn).is_err());
2965    }
2966
2967    #[diesel_test_helper::test]
2968    fn set_limit_enforces_compound_select() {
2969        let mut conn = connection();
2970        conn.set_limit(SqliteLimit::CompoundSelect, 2);
2971
2972        assert!(
2973            crate::sql_query("SELECT 1 UNION SELECT 2")
2974                .execute(&mut conn)
2975                .is_ok()
2976        );
2977        // Five UNION terms exceed the limit of two ("too many terms in compound SELECT").
2978        assert!(
2979            crate::sql_query(
2980                "SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5"
2981            )
2982            .execute(&mut conn)
2983            .is_err()
2984        );
2985    }
2986
2987    #[diesel_test_helper::test]
2988    fn set_limit_enforces_vdbe_op() {
2989        // The same heavy statement runs under the default opcode budget but fails once that
2990        // budget is restricted to a tiny value (reported as SQLITE_NOMEM).
2991        let heavy = "SELECT count(*) FROM sqlite_master a, sqlite_master b, sqlite_master c";
2992
2993        let mut unconstrained = connection();
2994        assert!(crate::sql_query(heavy).execute(&mut unconstrained).is_ok());
2995
2996        let mut conn = connection();
2997        conn.set_limit(SqliteLimit::VdbeOp, 5);
2998        assert!(crate::sql_query(heavy).execute(&mut conn).is_err());
2999    }
3000
3001    #[diesel_test_helper::test]
3002    fn set_limit_enforces_function_arg() {
3003        let mut conn = connection();
3004        conn.set_limit(SqliteLimit::FunctionArg, 3);
3005
3006        assert!(
3007            crate::sql_query("SELECT max(1, 2, 3)")
3008                .execute(&mut conn)
3009                .is_ok()
3010        );
3011        // Eight arguments exceed the limit of three ("too many arguments on function max").
3012        assert!(
3013            crate::sql_query("SELECT max(1, 2, 3, 4, 5, 6, 7, 8)")
3014                .execute(&mut conn)
3015                .is_err()
3016        );
3017    }
3018
3019    #[diesel_test_helper::test]
3020    fn set_limit_enforces_attached() {
3021        let mut conn = connection();
3022        conn.set_limit(SqliteLimit::Attached, 0);
3023
3024        // With zero attachments allowed, any ATTACH is rejected ("too many attached databases").
3025        assert!(
3026            crate::sql_query("ATTACH DATABASE ':memory:' AS aux_db")
3027                .execute(&mut conn)
3028                .is_err()
3029        );
3030    }
3031
3032    #[diesel_test_helper::test]
3033    fn set_limit_enforces_variable_number() {
3034        let mut conn = connection();
3035        // The published default sits below the bundled ceiling, so it is applied verbatim and
3036        // acts as the boundary: a parameter index at the limit is accepted, one past it is
3037        // rejected ("variable number must be between ?1 and ?N").
3038        conn.set_limit(
3039            SqliteLimit::VariableNumber,
3040            SqliteLimit::DEFAULT_VARIABLE_NUMBER_LIMIT,
3041        );
3042        let at_limit = format!("SELECT ?{}", SqliteLimit::DEFAULT_VARIABLE_NUMBER_LIMIT);
3043        let past_limit = format!(
3044            "SELECT ?{}",
3045            SqliteLimit::DEFAULT_VARIABLE_NUMBER_LIMIT as i64 + 1
3046        );
3047        assert!(crate::sql_query(&at_limit).execute(&mut conn).is_ok());
3048        assert!(crate::sql_query(&past_limit).execute(&mut conn).is_err());
3049    }
3050
3051    #[diesel_test_helper::test]
3052    fn set_limit_enforces_trigger_depth() {
3053        use crate::connection::SimpleConnection;
3054
3055        // A recursive trigger that terminates on its own at x = 100.
3056        let setup = "PRAGMA recursive_triggers = ON;\
3057             CREATE TABLE recur (x INTEGER);\
3058             CREATE TRIGGER recur_tr AFTER INSERT ON recur WHEN NEW.x < 100 \
3059             BEGIN INSERT INTO recur VALUES (NEW.x + 1); END;";
3060
3061        // Under the default depth the recursion completes.
3062        let mut unconstrained = connection();
3063        unconstrained.batch_execute(setup).unwrap();
3064        assert!(
3065            crate::sql_query("INSERT INTO recur VALUES (1)")
3066                .execute(&mut unconstrained)
3067                .is_ok()
3068        );
3069
3070        // A tiny depth limit is hit before the recursion can terminate
3071        // ("too many levels of trigger recursion").
3072        let mut conn = connection();
3073        conn.set_limit(SqliteLimit::TriggerDepth, 3);
3074        conn.batch_execute(setup).unwrap();
3075        assert!(
3076            crate::sql_query("INSERT INTO recur VALUES (1)")
3077                .execute(&mut conn)
3078                .is_err()
3079        );
3080    }
3081
3082    #[diesel_test_helper::test]
3083    fn worker_threads_limit_has_no_runtime_error_path() {
3084        // Unlike the other categories, WorkerThreads only caps the number of auxiliary sort
3085        // threads a statement may start. Lowering it never raises an error, it only affects
3086        // performance. There is therefore no enforcement failure to assert, only that the value
3087        // is applied and ordinary queries keep working.
3088        let mut conn = connection();
3089        conn.set_limit(SqliteLimit::WorkerThreads, 0);
3090        assert_eq!(conn.get_limit(SqliteLimit::WorkerThreads), 0);
3091        assert!(crate::sql_query("SELECT 1").execute(&mut conn).is_ok());
3092    }
3093
3094    #[diesel_test_helper::test]
3095    fn set_limit_enforces_sql_length() {
3096        let mut conn = connection();
3097        conn.set_limit(SqliteLimit::SqlLength, 20);
3098
3099        // A statement longer than 20 bytes is rejected by SQLite.
3100        let result =
3101            crate::sql_query("SELECT * FROM sqlite_master WHERE type = 'table'").execute(&mut conn);
3102        assert!(result.is_err());
3103    }
3104
3105    #[diesel_test_helper::test]
3106    fn set_limit_enforces_like_pattern_length() {
3107        let mut conn = connection();
3108        conn.set_limit(SqliteLimit::LikePatternLength, 100);
3109
3110        assert!(
3111            crate::sql_query("SELECT 'test' LIKE 'te%'")
3112                .execute(&mut conn)
3113                .is_ok()
3114        );
3115
3116        let long_pattern = "%".repeat(200);
3117        let query = format!("SELECT 'test' LIKE '{long_pattern}'");
3118        assert!(crate::sql_query(&query).execute(&mut conn).is_err());
3119    }
3120
3121    #[diesel_test_helper::test]
3122    fn set_limit_clamps_above_compile_time_maximum() {
3123        let mut conn = connection();
3124        // SQLite clamps a requested value to its hard compile-time ceiling
3125        // rather than accepting it verbatim.
3126        conn.set_limit(SqliteLimit::Length, i32::MAX);
3127        let clamped = conn.get_limit(SqliteLimit::Length);
3128        assert!(clamped > 0 && clamped < i32::MAX);
3129    }
3130
3131    #[diesel_test_helper::test]
3132    fn set_recommended_security_limits_applies_documented_table() {
3133        let mut conn = connection();
3134        conn.set_recommended_security_limits();
3135
3136        assert_eq!(conn.get_limit(SqliteLimit::Length), 1_000_000);
3137        assert_eq!(conn.get_limit(SqliteLimit::SqlLength), 100_000);
3138        assert_eq!(conn.get_limit(SqliteLimit::ColumnCount), 100);
3139        assert_eq!(conn.get_limit(SqliteLimit::ExprDepth), 10);
3140        assert_eq!(conn.get_limit(SqliteLimit::CompoundSelect), 3);
3141        assert_eq!(conn.get_limit(SqliteLimit::VdbeOp), 25_000);
3142        assert_eq!(conn.get_limit(SqliteLimit::FunctionArg), 8);
3143        assert_eq!(conn.get_limit(SqliteLimit::Attached), 0);
3144        assert_eq!(conn.get_limit(SqliteLimit::LikePatternLength), 50);
3145        assert_eq!(conn.get_limit(SqliteLimit::VariableNumber), 10);
3146        assert_eq!(conn.get_limit(SqliteLimit::TriggerDepth), 10);
3147    }
3148
3149    #[diesel_test_helper::test]
3150    fn safe_limit_constants_do_not_exceed_defaults() {
3151        // The hardened value for each category is a tightening of SQLite's published default, so
3152        // it must never be larger. This is asserted instead of comparing the `DEFAULT_*`
3153        // constants to a fresh connection, because the runtime default of categories such as
3154        // `FunctionArg` and `VariableNumber` is build-dependent (the bundled libsqlite3-sys
3155        // raises several of them), while these published constants are fixed.
3156        let pairs = [
3157            (
3158                SqliteLimit::SAFE_LENGTH_LIMIT,
3159                SqliteLimit::DEFAULT_LENGTH_LIMIT,
3160            ),
3161            (
3162                SqliteLimit::SAFE_SQL_LENGTH_LIMIT,
3163                SqliteLimit::DEFAULT_SQL_LENGTH_LIMIT,
3164            ),
3165            (
3166                SqliteLimit::SAFE_COLUMN_COUNT_LIMIT,
3167                SqliteLimit::DEFAULT_COLUMN_COUNT_LIMIT,
3168            ),
3169            (
3170                SqliteLimit::SAFE_EXPR_DEPTH_LIMIT,
3171                SqliteLimit::DEFAULT_EXPR_DEPTH_LIMIT,
3172            ),
3173            (
3174                SqliteLimit::SAFE_COMPOUND_SELECT_LIMIT,
3175                SqliteLimit::DEFAULT_COMPOUND_SELECT_LIMIT,
3176            ),
3177            (
3178                SqliteLimit::SAFE_VDBE_OP_LIMIT,
3179                SqliteLimit::DEFAULT_VDBE_OP_LIMIT,
3180            ),
3181            (
3182                SqliteLimit::SAFE_FUNCTION_ARG_LIMIT,
3183                SqliteLimit::DEFAULT_FUNCTION_ARG_LIMIT,
3184            ),
3185            (
3186                SqliteLimit::SAFE_ATTACHED_LIMIT,
3187                SqliteLimit::DEFAULT_ATTACHED_LIMIT,
3188            ),
3189            (
3190                SqliteLimit::SAFE_LIKE_PATTERN_LENGTH_LIMIT,
3191                SqliteLimit::DEFAULT_LIKE_PATTERN_LENGTH_LIMIT,
3192            ),
3193            (
3194                SqliteLimit::SAFE_VARIABLE_NUMBER_LIMIT,
3195                SqliteLimit::DEFAULT_VARIABLE_NUMBER_LIMIT,
3196            ),
3197            (
3198                SqliteLimit::SAFE_TRIGGER_DEPTH_LIMIT,
3199                SqliteLimit::DEFAULT_TRIGGER_DEPTH_LIMIT,
3200            ),
3201            (
3202                SqliteLimit::SAFE_WORKER_THREADS_LIMIT,
3203                SqliteLimit::DEFAULT_WORKER_THREADS_LIMIT,
3204            ),
3205        ];
3206        for (safe, default) in pairs {
3207            assert!(
3208                safe <= default,
3209                "safe value {safe} exceeds default {default}"
3210            );
3211        }
3212    }
3213
3214    #[diesel_test_helper::test]
3215    fn safe_limit_constants_match_recommended_setter() {
3216        let mut conn = connection();
3217        conn.set_recommended_security_limits();
3218
3219        assert_eq!(
3220            conn.get_limit(SqliteLimit::Length),
3221            SqliteLimit::SAFE_LENGTH_LIMIT
3222        );
3223        assert_eq!(
3224            conn.get_limit(SqliteLimit::SqlLength),
3225            SqliteLimit::SAFE_SQL_LENGTH_LIMIT
3226        );
3227        assert_eq!(
3228            conn.get_limit(SqliteLimit::ColumnCount),
3229            SqliteLimit::SAFE_COLUMN_COUNT_LIMIT
3230        );
3231        assert_eq!(
3232            conn.get_limit(SqliteLimit::ExprDepth),
3233            SqliteLimit::SAFE_EXPR_DEPTH_LIMIT
3234        );
3235        assert_eq!(
3236            conn.get_limit(SqliteLimit::CompoundSelect),
3237            SqliteLimit::SAFE_COMPOUND_SELECT_LIMIT
3238        );
3239        assert_eq!(
3240            conn.get_limit(SqliteLimit::VdbeOp),
3241            SqliteLimit::SAFE_VDBE_OP_LIMIT
3242        );
3243        assert_eq!(
3244            conn.get_limit(SqliteLimit::FunctionArg),
3245            SqliteLimit::SAFE_FUNCTION_ARG_LIMIT
3246        );
3247        assert_eq!(
3248            conn.get_limit(SqliteLimit::Attached),
3249            SqliteLimit::SAFE_ATTACHED_LIMIT
3250        );
3251        assert_eq!(
3252            conn.get_limit(SqliteLimit::LikePatternLength),
3253            SqliteLimit::SAFE_LIKE_PATTERN_LENGTH_LIMIT
3254        );
3255        assert_eq!(
3256            conn.get_limit(SqliteLimit::VariableNumber),
3257            SqliteLimit::SAFE_VARIABLE_NUMBER_LIMIT
3258        );
3259        assert_eq!(
3260            conn.get_limit(SqliteLimit::TriggerDepth),
3261            SqliteLimit::SAFE_TRIGGER_DEPTH_LIMIT
3262        );
3263        // The recommended setter leaves `WorkerThreads` untouched because its default is already
3264        // safe, so assert that the documented safe value matches what the connection reports.
3265        assert_eq!(
3266            conn.get_limit(SqliteLimit::WorkerThreads),
3267            SqliteLimit::SAFE_WORKER_THREADS_LIMIT
3268        );
3269    }
3270
3271    // ---- db_config tests ----
3272
3273    #[diesel_test_helper::test]
3274    fn db_config_defensive_roundtrip() {
3275        let conn = &mut connection();
3276        conn.set_defensive(true).unwrap();
3277        assert!(conn.is_defensive().unwrap());
3278        conn.set_defensive(false).unwrap();
3279        assert!(!conn.is_defensive().unwrap());
3280    }
3281
3282    #[diesel_test_helper::test]
3283    fn db_config_trusted_schema_roundtrip() {
3284        let conn = &mut connection();
3285        conn.set_trusted_schema(false).unwrap();
3286        assert!(!conn.is_trusted_schema().unwrap());
3287        conn.set_trusted_schema(true).unwrap();
3288        assert!(conn.is_trusted_schema().unwrap());
3289    }
3290
3291    #[diesel_test_helper::test]
3292    fn db_config_with_load_extension_enabled_scopes_the_flag() {
3293        let conn = &mut connection();
3294        conn.with_load_extension_enabled(|conn| {
3295            // Enabled for the duration of the closure.
3296            assert!(conn.is_load_extension_enabled().unwrap());
3297            QueryResult::Ok(())
3298        })
3299        .unwrap();
3300        // Disabled again afterwards.
3301        assert!(!conn.is_load_extension_enabled().unwrap());
3302    }
3303
3304    #[cfg(all(
3305        feature = "std",
3306        not(all(target_family = "wasm", target_os = "unknown"))
3307    ))]
3308    #[diesel_test_helper::test]
3309    fn with_load_extension_enabled_disables_after_panic() {
3310        let conn = &mut connection();
3311        let outcome = std::panic::catch_unwind(core::panic::AssertUnwindSafe(|| {
3312            conn.with_load_extension_enabled(|_conn| -> QueryResult<()> {
3313                panic!("boom inside closure");
3314            })
3315        }));
3316        assert!(outcome.is_err(), "panic should propagate");
3317        assert!(
3318            !conn.is_load_extension_enabled().unwrap(),
3319            "extension loading must be disabled again after a panic"
3320        );
3321    }
3322
3323    #[diesel_test_helper::test]
3324    fn db_config_triggers_roundtrip() {
3325        let conn = &mut connection();
3326        conn.set_triggers_enabled(false).unwrap();
3327        assert!(!conn.are_triggers_enabled().unwrap());
3328        conn.set_triggers_enabled(true).unwrap();
3329        assert!(conn.are_triggers_enabled().unwrap());
3330    }
3331
3332    #[diesel_test_helper::test]
3333    fn db_config_views_roundtrip() {
3334        let conn = &mut connection();
3335        conn.set_views_enabled(false).unwrap();
3336        assert!(!conn.are_views_enabled().unwrap());
3337        conn.set_views_enabled(true).unwrap();
3338        assert!(conn.are_views_enabled().unwrap());
3339    }
3340
3341    #[diesel_test_helper::test]
3342    fn db_config_foreign_keys_roundtrip() {
3343        let conn = &mut connection();
3344        conn.set_foreign_keys_enabled(true).unwrap();
3345        assert!(conn.are_foreign_keys_enabled().unwrap());
3346        conn.set_foreign_keys_enabled(false).unwrap();
3347        assert!(!conn.are_foreign_keys_enabled().unwrap());
3348    }
3349
3350    #[diesel_test_helper::test]
3351    fn db_config_dqs_dml_roundtrip() {
3352        let conn = &mut connection();
3353        conn.set_double_quoted_strings_dml(false).unwrap();
3354        assert!(!conn.are_double_quoted_strings_dml_enabled().unwrap());
3355        conn.set_double_quoted_strings_dml(true).unwrap();
3356        assert!(conn.are_double_quoted_strings_dml_enabled().unwrap());
3357    }
3358
3359    #[diesel_test_helper::test]
3360    fn db_config_dqs_ddl_roundtrip() {
3361        let conn = &mut connection();
3362        conn.set_double_quoted_strings_ddl(false).unwrap();
3363        assert!(!conn.are_double_quoted_strings_ddl_enabled().unwrap());
3364        conn.set_double_quoted_strings_ddl(true).unwrap();
3365        assert!(conn.are_double_quoted_strings_ddl_enabled().unwrap());
3366    }
3367
3368    #[diesel_test_helper::test]
3369    fn db_config_fts3_tokenizer_roundtrip() {
3370        let conn = &mut connection();
3371        conn.set_fts3_tokenizer_enabled(false).unwrap();
3372        assert!(!conn.is_fts3_tokenizer_enabled().unwrap());
3373        conn.set_fts3_tokenizer_enabled(true).unwrap();
3374        assert!(conn.is_fts3_tokenizer_enabled().unwrap());
3375    }
3376
3377    #[diesel_test_helper::test]
3378    fn db_config_writable_schema_roundtrip() {
3379        let conn = &mut connection();
3380        conn.set_writable_schema(false).unwrap();
3381        assert!(!conn.is_writable_schema().unwrap());
3382        conn.set_writable_schema(true).unwrap();
3383        assert!(conn.is_writable_schema().unwrap());
3384    }
3385
3386    #[diesel_test_helper::test]
3387    fn db_config_attach_create_roundtrip() {
3388        let conn = &mut connection();
3389        // ATTACH_CREATE requires SQLite 3.46.0+; skip if unsupported
3390        if conn.set_attach_create_enabled(false).is_err() {
3391            return;
3392        }
3393        assert!(!conn.is_attach_create_enabled().unwrap());
3394        conn.set_attach_create_enabled(true).unwrap();
3395        assert!(conn.is_attach_create_enabled().unwrap());
3396    }
3397
3398    #[diesel_test_helper::test]
3399    fn db_config_attach_write_roundtrip() {
3400        let conn = &mut connection();
3401        // ATTACH_WRITE requires SQLite 3.46.0+; skip if unsupported
3402        if conn.set_attach_write_enabled(false).is_err() {
3403            return;
3404        }
3405        assert!(!conn.is_attach_write_enabled().unwrap());
3406        conn.set_attach_write_enabled(true).unwrap();
3407        assert!(conn.is_attach_write_enabled().unwrap());
3408    }
3409
3410    // ---- behavioral db_config tests ----
3411
3412    #[diesel_test_helper::test]
3413    fn defensive_mode_blocks_writable_schema() {
3414        let conn = &mut connection();
3415        conn.set_defensive(true).unwrap();
3416        // In defensive mode, writable_schema should remain off even if we try to set it
3417        let _ = crate::sql_query("PRAGMA writable_schema = ON").execute(conn);
3418        assert!(!conn.is_writable_schema().unwrap());
3419    }
3420
3421    #[diesel_test_helper::test]
3422    fn foreign_keys_enabled_enforces_constraints() {
3423        let conn = &mut connection();
3424        conn.set_foreign_keys_enabled(true).unwrap();
3425
3426        crate::sql_query("CREATE TABLE parent (id INTEGER PRIMARY KEY)")
3427            .execute(conn)
3428            .unwrap();
3429        crate::sql_query(
3430            "CREATE TABLE child (id INTEGER PRIMARY KEY, parent_id INTEGER REFERENCES parent(id))",
3431        )
3432        .execute(conn)
3433        .unwrap();
3434
3435        // Insert a child row with no matching parent — should fail with FK enabled
3436        let result =
3437            crate::sql_query("INSERT INTO child (id, parent_id) VALUES (1, 999)").execute(conn);
3438        assert!(result.is_err());
3439    }
3440
3441    #[diesel_test_helper::test]
3442    fn views_disabled_blocks_view_queries() {
3443        let conn = &mut connection();
3444        crate::sql_query("CREATE TABLE base (id INTEGER PRIMARY KEY)")
3445            .execute(conn)
3446            .unwrap();
3447        crate::sql_query("INSERT INTO base (id) VALUES (1)")
3448            .execute(conn)
3449            .unwrap();
3450        crate::sql_query("CREATE VIEW base_view AS SELECT id FROM base")
3451            .execute(conn)
3452            .unwrap();
3453
3454        // Enabled (default): the view can be queried.
3455        conn.set_views_enabled(true).unwrap();
3456        assert!(
3457            crate::sql_query("SELECT id FROM base_view")
3458                .execute(conn)
3459                .is_ok()
3460        );
3461
3462        // Disabled: queries that reference the view fail.
3463        conn.set_views_enabled(false).unwrap();
3464        assert!(
3465            crate::sql_query("SELECT id FROM base_view")
3466                .execute(conn)
3467                .is_err()
3468        );
3469    }
3470
3471    #[diesel_test_helper::test]
3472    fn triggers_disabled_prevents_firing() {
3473        let conn = &mut connection();
3474        crate::sql_query("CREATE TABLE source (id INTEGER PRIMARY KEY)")
3475            .execute(conn)
3476            .unwrap();
3477        crate::sql_query("CREATE TABLE trigger_log (n INTEGER)")
3478            .execute(conn)
3479            .unwrap();
3480        crate::sql_query("CREATE TRIGGER log_insert AFTER INSERT ON source BEGIN INSERT INTO trigger_log (n) VALUES (1); END")
3481            .execute(conn)
3482            .unwrap();
3483
3484        // Disabled: inserting into `source` must not fire the trigger.
3485        conn.set_triggers_enabled(false).unwrap();
3486        crate::sql_query("INSERT INTO source (id) VALUES (1)")
3487            .execute(conn)
3488            .unwrap();
3489        let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM trigger_log")
3490            .get_result(conn)
3491            .unwrap();
3492        assert_eq!(0, count, "trigger should not fire while disabled");
3493
3494        // Enabled: the trigger fires and writes one row.
3495        conn.set_triggers_enabled(true).unwrap();
3496        crate::sql_query("INSERT INTO source (id) VALUES (2)")
3497            .execute(conn)
3498            .unwrap();
3499        let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM trigger_log")
3500            .get_result(conn)
3501            .unwrap();
3502        assert_eq!(1, count, "trigger should fire while enabled");
3503    }
3504
3505    #[diesel_test_helper::test]
3506    fn dqs_dml_controls_double_quoted_string_literals() {
3507        let conn = &mut connection();
3508
3509        // Disabled: a double-quoted token in DML is parsed as an identifier, so a
3510        // bare `"text"` that is not a column errors.
3511        conn.set_double_quoted_strings_dml(false).unwrap();
3512        let disabled = sql::<Text>(r#"SELECT "bare_token""#).get_result::<String>(conn);
3513        assert!(disabled.is_err());
3514
3515        // Enabled: the same token is accepted as a string literal.
3516        conn.set_double_quoted_strings_dml(true).unwrap();
3517        let enabled = sql::<Text>(r#"SELECT "bare_token""#).get_result::<String>(conn);
3518        assert_eq!(Ok("bare_token".to_owned()), enabled);
3519    }
3520
3521    #[diesel_test_helper::test]
3522    fn dqs_ddl_controls_double_quoted_string_literals() {
3523        let conn = &mut connection();
3524
3525        // Disabled: a double-quoted token in a CHECK constraint is parsed as an
3526        // identifier. As there is no such column, creating the table errors.
3527        conn.set_double_quoted_strings_ddl(false).unwrap();
3528        let disabled =
3529            crate::sql_query(r#"CREATE TABLE dqs_off (name TEXT, CHECK (name <> "not_a_column"))"#)
3530                .execute(conn);
3531        assert!(disabled.is_err());
3532
3533        // Enabled: the same token is accepted as a string literal, so the CHECK
3534        // constraint (and the table) are created successfully.
3535        conn.set_double_quoted_strings_ddl(true).unwrap();
3536        let enabled =
3537            crate::sql_query(r#"CREATE TABLE dqs_on (name TEXT, CHECK (name <> "not_a_column"))"#)
3538                .execute(conn);
3539        assert!(enabled.is_ok());
3540    }
3541
3542    #[diesel_test_helper::test]
3543    fn writable_schema_controls_direct_sqlite_master_writes() {
3544        let conn = &mut connection();
3545        crate::sql_query("CREATE TABLE protected (id INTEGER PRIMARY KEY)")
3546            .execute(conn)
3547            .unwrap();
3548
3549        let update =
3550            "UPDATE sqlite_master SET sql = sql WHERE type = 'table' AND name = 'protected'";
3551
3552        // Disabled (default): a direct write to sqlite_master is rejected.
3553        conn.set_writable_schema(false).unwrap();
3554        assert!(crate::sql_query(update).execute(conn).is_err());
3555
3556        // Enabled: the same write is permitted.
3557        conn.set_writable_schema(true).unwrap();
3558        assert!(crate::sql_query(update).execute(conn).is_ok());
3559    }
3560
3561    #[diesel_test_helper::test]
3562    fn fts3_tokenizer_disabled_blocks_the_function() {
3563        let conn = &mut connection();
3564
3565        // Enable first to detect whether FTS3 is compiled into this SQLite build.
3566        conn.set_fts3_tokenizer_enabled(true).unwrap();
3567        let enabled = sql::<crate::sql_types::Binary>("SELECT fts3_tokenizer('simple')")
3568            .get_result::<Vec<u8>>(conn);
3569        if enabled.is_err() {
3570            // FTS3 is not available in this build, so there is nothing to assert.
3571            return;
3572        }
3573
3574        // Disabled: the `fts3_tokenizer()` SQL function is no longer callable.
3575        conn.set_fts3_tokenizer_enabled(false).unwrap();
3576        let disabled = sql::<crate::sql_types::Binary>("SELECT fts3_tokenizer('simple')")
3577            .get_result::<Vec<u8>>(conn);
3578        assert!(disabled.is_err());
3579    }
3580
3581    // These ATTACH tests need a real filesystem (temp files), which is not
3582    // available on the wasm target, where SQLite is in-memory only.
3583    #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3584    fn temp_db_path(name: &str) -> (tempfile::TempDir, std::path::PathBuf) {
3585        let dir = tempfile::tempdir().unwrap();
3586        let path = dir.path().join(name);
3587        (dir, path)
3588    }
3589
3590    #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3591    #[diesel_test_helper::test]
3592    fn attach_create_disabled_blocks_new_database_files() {
3593        let conn = &mut connection();
3594
3595        // The ATTACH_CREATE option was added in SQLite 3.49.0; skip on older
3596        // libraries (e.g. the system SQLite on the Ubuntu 24.04 CI runners).
3597        if conn.set_attach_create_enabled(false).is_err() {
3598            return;
3599        }
3600
3601        let (_dir, path) = temp_db_path("create.db");
3602
3603        // Disabled: attaching a path that does not exist yet must fail.
3604        assert!(
3605            conn.attach_database(path.to_str().unwrap(), "aux_create")
3606                .is_err()
3607        );
3608
3609        // Enabled: the same ATTACH now creates and opens the file.
3610        conn.set_attach_create_enabled(true).unwrap();
3611        conn.attach_database(path.to_str().unwrap(), "aux_create")
3612            .unwrap();
3613        conn.detach_database("aux_create").unwrap();
3614    }
3615
3616    #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3617    #[diesel_test_helper::test]
3618    fn attach_write_disabled_opens_attached_databases_read_only() {
3619        let conn = &mut connection();
3620
3621        // The ATTACH_WRITE option was added in SQLite 3.49.0; skip on older
3622        // libraries (e.g. the system SQLite on the Ubuntu 24.04 CI runners).
3623        // This guard also leaves ATTACH_WRITE disabled for the first check below.
3624        if conn.set_attach_write_enabled(false).is_err() {
3625            return;
3626        }
3627
3628        // Seed an existing on-disk database with a table to write into.
3629        let (_dir, path) = temp_db_path("write.db");
3630        {
3631            let mut seed = SqliteConnection::establish(path.to_str().unwrap()).unwrap();
3632            crate::sql_query("CREATE TABLE t (id INTEGER)")
3633                .execute(&mut seed)
3634                .unwrap();
3635        }
3636
3637        // Disabled: the attached database is opened read-only, so writes fail.
3638        conn.attach_database(path.to_str().unwrap(), "aux_write")
3639            .unwrap();
3640        assert!(
3641            crate::sql_query("INSERT INTO aux_write.t (id) VALUES (1)")
3642                .execute(conn)
3643                .is_err()
3644        );
3645        conn.detach_database("aux_write").unwrap();
3646
3647        // Enabled: the attached database is writable again.
3648        conn.set_attach_write_enabled(true).unwrap();
3649        conn.attach_database(path.to_str().unwrap(), "aux_write")
3650            .unwrap();
3651        crate::sql_query("INSERT INTO aux_write.t (id) VALUES (1)")
3652            .execute(conn)
3653            .unwrap();
3654        conn.detach_database("aux_write").unwrap();
3655    }
3656
3657    // Tables used by the ATTACH round-trip tests below. Their CREATE statements are
3658    // DDL (raw SQL), but rows and reads go through the typed query DSL.
3659    #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3660    table! {
3661        attach_owners (id) {
3662            id -> Integer,
3663            name -> Text,
3664        }
3665    }
3666    #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3667    table! {
3668        aux.attach_pets (id) {
3669            id -> Integer,
3670            owner_id -> Integer,
3671            name -> Text,
3672        }
3673    }
3674    #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3675    allow_tables_to_appear_in_same_query!(attach_owners, attach_pets);
3676    #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3677    table! {
3678        attach_marker (id) {
3679            id -> Integer,
3680        }
3681    }
3682    #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3683    table! {
3684        ro.readonly_marker (id) {
3685            id -> Integer,
3686        }
3687    }
3688
3689    #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3690    #[diesel_test_helper::test]
3691    fn attach_database_supports_cross_schema_join_then_detach() {
3692        use crate::connection::SimpleConnection;
3693
3694        let conn = &mut connection();
3695
3696        conn.attach_database(":memory:", "aux").unwrap();
3697
3698        // Schema-qualified CREATE is DDL and stays raw SQL. The rows and the join
3699        // below use the typed query DSL.
3700        conn.batch_execute(
3701            "CREATE TABLE attach_owners (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
3702             CREATE TABLE aux.attach_pets (id INTEGER PRIMARY KEY, owner_id INTEGER, name TEXT NOT NULL);",
3703        )
3704        .unwrap();
3705
3706        crate::insert_into(attach_owners::table)
3707            .values(&[
3708                (attach_owners::id.eq(1), attach_owners::name.eq("Sean")),
3709                (attach_owners::id.eq(2), attach_owners::name.eq("Tess")),
3710            ])
3711            .execute(conn)
3712            .unwrap();
3713        crate::insert_into(attach_pets::table)
3714            .values((
3715                attach_pets::id.eq(1),
3716                attach_pets::owner_id.eq(1),
3717                attach_pets::name.eq("Ferris"),
3718            ))
3719            .execute(conn)
3720            .unwrap();
3721
3722        let pet_owner = attach_owners::table
3723            .inner_join(attach_pets::table.on(attach_pets::owner_id.eq(attach_owners::id)))
3724            .filter(attach_pets::name.eq("Ferris"))
3725            .select(attach_owners::name)
3726            .get_result::<String>(conn)
3727            .unwrap();
3728        assert_eq!(pet_owner, "Sean");
3729
3730        conn.detach_database("aux").unwrap();
3731
3732        // The attached schema is gone, so querying it now fails.
3733        assert!(
3734            attach_pets::table
3735                .select(attach_pets::name)
3736                .get_result::<String>(conn)
3737                .is_err()
3738        );
3739    }
3740
3741    #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3742    #[diesel_test_helper::test]
3743    fn attach_database_binds_path_verbatim_without_quoting() {
3744        // A single quote in the path would break a hand-assembled ATTACH statement.
3745        // Bound parameters take the path verbatim.
3746        let (_dir, path) = temp_db_path("o'brien.db");
3747
3748        conn_attach_roundtrip(&path);
3749
3750        // The table created through ATTACH persisted to the literal file, so a
3751        // fresh connection to that exact path can read it.
3752        let mut direct = SqliteConnection::establish(path.to_str().unwrap()).unwrap();
3753        let count = attach_marker::table
3754            .count()
3755            .get_result::<i64>(&mut direct)
3756            .unwrap();
3757        assert_eq!(count, 0);
3758    }
3759
3760    #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3761    fn conn_attach_roundtrip(path: &std::path::Path) {
3762        use crate::connection::SimpleConnection;
3763
3764        let conn = &mut connection();
3765        conn.attach_database(path.to_str().unwrap(), "verbatim")
3766            .unwrap();
3767        conn.batch_execute("CREATE TABLE verbatim.attach_marker (id INTEGER PRIMARY KEY)")
3768            .unwrap();
3769        conn.detach_database("verbatim").unwrap();
3770    }
3771
3772    #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3773    #[diesel_test_helper::test]
3774    fn attach_database_interprets_file_uri_query_parameters() {
3775        // Seed a database with a row to read through the attached schema.
3776        let (_dir, path) = temp_db_path("uri_seed.db");
3777        {
3778            let mut seed = SqliteConnection::establish(path.to_str().unwrap()).unwrap();
3779            crate::sql_query("CREATE TABLE t (id INTEGER PRIMARY KEY)")
3780                .execute(&mut seed)
3781                .unwrap();
3782            crate::sql_query("INSERT INTO t (id) VALUES (1)")
3783                .execute(&mut seed)
3784                .unwrap();
3785        }
3786
3787        // Attach with a `file:` URI and `mode=ro`. If SQLite treated the bound
3788        // string as a literal filename it would fail to find the file; interpreting
3789        // it as a URI opens the real file in read-only mode instead.
3790        let uri = format!("file:{}?mode=ro", path.display());
3791        let conn = &mut connection();
3792        conn.attach_database(&uri, "ro_schema").unwrap();
3793
3794        // Read from the attached schema: proves the ATTACH opened a real file.
3795        let id: i64 = sql::<crate::sql_types::BigInt>("SELECT id FROM ro_schema.t")
3796            .get_result(conn)
3797            .unwrap();
3798        assert_eq!(id, 1);
3799
3800        // Write fails: the URI `mode=ro` parameter took effect.
3801        assert!(
3802            crate::sql_query("INSERT INTO ro_schema.t (id) VALUES (2)")
3803                .execute(conn)
3804                .is_err()
3805        );
3806
3807        conn.detach_database("ro_schema").unwrap();
3808    }
3809
3810    #[diesel_test_helper::test]
3811    fn attach_and_detach_surface_errors_without_panicking() {
3812        let conn = &mut connection();
3813
3814        // A duplicate schema name on attach is an error, not a panic.
3815        conn.attach_database(":memory:", "dup").unwrap();
3816        assert!(conn.attach_database(":memory:", "dup").is_err());
3817        conn.detach_database("dup").unwrap();
3818
3819        // Detaching an unknown schema is likewise an error. Detaching one still in
3820        // use cannot occur through the safe API: an in-flight iterator holds
3821        // `&mut conn`, so no detach can overlap it.
3822        assert!(conn.detach_database("never_attached").is_err());
3823    }
3824
3825    #[diesel_test_helper::test]
3826    fn attach_database_binds_schema_name_verbatim_without_identifier_quoting() {
3827        use crate::connection::SimpleConnection;
3828
3829        let conn = &mut connection();
3830
3831        // A space or quote in the schema name would need identifier quoting in a
3832        // hand-assembled statement. Bound as a parameter it is taken verbatim. The
3833        // reference below stays raw SQL: such a schema is not expressible via `table!`.
3834        let schema = "weird 'schema";
3835        conn.attach_database(":memory:", schema).unwrap();
3836
3837        conn.batch_execute(
3838            r#"CREATE TABLE "weird 'schema".t (id INTEGER PRIMARY KEY);
3839             INSERT INTO "weird 'schema".t (id) VALUES (7);"#,
3840        )
3841        .unwrap();
3842        let id = sql::<Integer>(r#"SELECT id FROM "weird 'schema".t"#)
3843            .get_result::<i32>(conn)
3844            .unwrap();
3845        assert_eq!(id, 7);
3846
3847        conn.detach_database(schema).unwrap();
3848    }
3849
3850    #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3851    #[diesel_test_helper::test]
3852    fn attach_database_honors_create_and_write_hardening_knobs() {
3853        use crate::connection::SimpleConnection;
3854
3855        let conn = &mut connection();
3856
3857        // ATTACH_CREATE and ATTACH_WRITE need SQLite 3.49.0+, so skip on older libraries.
3858        if conn.set_attach_create_enabled(false).is_err() {
3859            return;
3860        }
3861
3862        // Create disabled: attaching a nonexistent path fails and materializes nothing.
3863        let (_dir_missing, missing) = temp_db_path("nocreate.db");
3864        assert!(
3865            conn.attach_database(missing.to_str().unwrap(), "missing")
3866                .is_err()
3867        );
3868        assert!(!missing.exists());
3869
3870        // Write disabled: an existing database attaches read-only, so writes fail.
3871        conn.set_attach_write_enabled(false).unwrap();
3872        let (_dir_existing, existing) = temp_db_path("readonly.db");
3873        {
3874            let mut seed = SqliteConnection::establish(existing.to_str().unwrap()).unwrap();
3875            seed.batch_execute("CREATE TABLE readonly_marker (id INTEGER)")
3876                .unwrap();
3877        }
3878        conn.attach_database(existing.to_str().unwrap(), "ro")
3879            .unwrap();
3880        assert!(
3881            crate::insert_into(readonly_marker::table)
3882                .values(readonly_marker::id.eq(1))
3883                .execute(conn)
3884                .is_err()
3885        );
3886        conn.detach_database("ro").unwrap();
3887    }
3888
3889    // ---- DIRECTONLY / INNOCUOUS function behavior tests ----
3890
3891    #[declare_sql_function]
3892    extern "SQL" {
3893        fn directonly_fn() -> Integer;
3894        fn innocuous_fn() -> Integer;
3895    }
3896
3897    #[diesel_test_helper::test]
3898    fn directonly_function_blocked_from_view() {
3899        let conn = &mut connection();
3900
3901        // Register a DIRECTONLY function
3902        directonly_fn_utils::register_impl_with_behavior(
3903            conn,
3904            SqliteFunctionBehavior::DIRECTONLY,
3905            || 42,
3906        )
3907        .unwrap();
3908
3909        // Direct call works
3910        let result = crate::select(directonly_fn()).get_result::<i32>(conn);
3911        assert_eq!(Ok(42), result);
3912
3913        // Create a view that calls the function
3914        crate::sql_query("CREATE VIEW test_view AS SELECT directonly_fn() AS val")
3915            .execute(conn)
3916            .unwrap();
3917
3918        // Disable trusted schema so DIRECTONLY is enforced from schema objects
3919        conn.set_trusted_schema(false).unwrap();
3920
3921        // Querying the view should fail because the function is DIRECTONLY
3922        let result = crate::sql_query("SELECT val FROM test_view").execute(conn);
3923        assert!(result.is_err());
3924    }
3925
3926    #[diesel_test_helper::test]
3927    fn innocuous_function_allowed_from_view_with_untrusted_schema() {
3928        let conn = &mut connection();
3929
3930        // Register an INNOCUOUS function
3931        innocuous_fn_utils::register_impl_with_behavior(
3932            conn,
3933            SqliteFunctionBehavior::DETERMINISTIC | SqliteFunctionBehavior::INNOCUOUS,
3934            || 99,
3935        )
3936        .unwrap();
3937
3938        // Create a view that calls the function
3939        crate::sql_query("CREATE VIEW innocuous_view AS SELECT innocuous_fn() AS val")
3940            .execute(conn)
3941            .unwrap();
3942
3943        // Disable trusted schema
3944        conn.set_trusted_schema(false).unwrap();
3945
3946        // Querying the view should succeed because the function is INNOCUOUS
3947        let result = crate::sql_query("SELECT val FROM innocuous_view").execute(conn);
3948        assert!(result.is_ok());
3949    }
3950
3951    #[diesel_test_helper::test]
3952    fn auto_vacuum_all_modes_roundtrip_on_fresh_database() {
3953        for mode in [
3954            AutoVacuumMode::None,
3955            AutoVacuumMode::Full,
3956            AutoVacuumMode::Incremental,
3957        ] {
3958            let conn = &mut connection();
3959            conn.set_auto_vacuum(None, mode).unwrap();
3960            assert_eq!(mode, conn.auto_vacuum(None).unwrap());
3961        }
3962    }
3963
3964    #[diesel_test_helper::test]
3965    fn auto_vacuum_incremental_sticks_across_schema_creation() {
3966        let conn = &mut connection();
3967        conn.set_auto_vacuum(None, AutoVacuumMode::Incremental)
3968            .unwrap();
3969        assert_eq!(AutoVacuumMode::Incremental, conn.auto_vacuum(None).unwrap());
3970
3971        crate::sql_query("CREATE TABLE t (id INTEGER PRIMARY KEY)")
3972            .execute(conn)
3973            .unwrap();
3974        assert_eq!(
3975            AutoVacuumMode::Incremental,
3976            conn.auto_vacuum(None).unwrap(),
3977            "the mode survives once the schema exists"
3978        );
3979    }
3980
3981    #[diesel_test_helper::test]
3982    fn auto_vacuum_change_from_none_requires_vacuum_on_populated_database() {
3983        let conn = &mut connection();
3984        crate::sql_query("CREATE TABLE t (id INTEGER PRIMARY KEY)")
3985            .execute(conn)
3986            .unwrap();
3987        crate::sql_query("INSERT INTO t (id) VALUES (1)")
3988            .execute(conn)
3989            .unwrap();
3990        assert_eq!(AutoVacuumMode::None, conn.auto_vacuum(None).unwrap());
3991
3992        // On a populated database the switch away from `None` is silently
3993        // deferred until a full rewrite.
3994        conn.set_auto_vacuum(None, AutoVacuumMode::Full).unwrap();
3995        assert_eq!(
3996            AutoVacuumMode::None,
3997            conn.auto_vacuum(None).unwrap(),
3998            "the change does not take effect without a VACUUM"
3999        );
4000
4001        crate::sql_query("VACUUM").execute(conn).unwrap();
4002        assert_eq!(
4003            AutoVacuumMode::Full,
4004            conn.auto_vacuum(None).unwrap(),
4005            "VACUUM rewrites the file and applies the mode"
4006        );
4007    }
4008
4009    #[diesel_test_helper::test]
4010    fn auto_vacuum_targets_the_named_attached_database() {
4011        let conn = &mut connection();
4012        crate::sql_query("ATTACH DATABASE ':memory:' AS aux")
4013            .execute(conn)
4014            .unwrap();
4015
4016        conn.set_auto_vacuum(Some("aux"), AutoVacuumMode::Full)
4017            .unwrap();
4018        assert_eq!(AutoVacuumMode::Full, conn.auto_vacuum(Some("aux")).unwrap());
4019        assert_eq!(
4020            AutoVacuumMode::None,
4021            conn.auto_vacuum(None).unwrap(),
4022            "main keeps its own default"
4023        );
4024    }
4025
4026    #[diesel_test_helper::test]
4027    fn auto_vacuum_schema_name_with_double_quote_is_handled() {
4028        let conn = &mut connection();
4029        let schema = r#"we"ird"#;
4030        crate::sql_query(alloc::format!(
4031            r#"ATTACH DATABASE ':memory:' AS "{}""#,
4032            schema.replace('"', "\"\"")
4033        ))
4034        .execute(conn)
4035        .unwrap();
4036
4037        conn.set_auto_vacuum(Some(schema), AutoVacuumMode::Incremental)
4038            .unwrap();
4039        assert_eq!(
4040            AutoVacuumMode::Incremental,
4041            conn.auto_vacuum(Some(schema)).unwrap()
4042        );
4043    }
4044
4045    table! {
4046        pragma_probe (id) {
4047            id -> Integer,
4048            payload -> Text,
4049        }
4050    }
4051
4052    table! {
4053        aux.aux_pragma_probe (id) {
4054            id -> Integer,
4055            payload -> Text,
4056        }
4057    }
4058
4059    const PROBE_TABLE: &str =
4060        "CREATE TABLE pragma_probe (id INTEGER PRIMARY KEY, payload TEXT NOT NULL)";
4061
4062    const AUX_PROBE_TABLE: &str =
4063        "CREATE TABLE aux.aux_pragma_probe (id INTEGER PRIMARY KEY, payload TEXT NOT NULL)";
4064
4065    // Large enough to spill onto overflow pages, so the database outgrows a single page
4066    // and leaves reclaimable pages behind once the row is deleted.
4067    fn overflowing_payload() -> String {
4068        "x".repeat(64 * 1024)
4069    }
4070
4071    fn insert_overflowing_row(conn: &mut SqliteConnection) {
4072        crate::insert_into(pragma_probe::table)
4073            .values((
4074                pragma_probe::id.eq(1),
4075                pragma_probe::payload.eq(overflowing_payload()),
4076            ))
4077            .execute(conn)
4078            .unwrap();
4079    }
4080
4081    #[diesel_test_helper::test]
4082    fn page_count_is_positive_and_grows() {
4083        let conn = &mut connection();
4084        conn.batch_execute(PROBE_TABLE).unwrap();
4085        let initial = conn.page_count(None).unwrap();
4086        assert!(initial > 0, "an initialized database has at least one page");
4087
4088        insert_overflowing_row(conn);
4089
4090        assert!(
4091            conn.page_count(None).unwrap() > initial,
4092            "a row spanning overflow pages grows the page count"
4093        );
4094    }
4095
4096    #[diesel_test_helper::test]
4097    fn freelist_count_tracks_reclaimable_space() {
4098        let conn = &mut connection();
4099        assert_eq!(
4100            0,
4101            conn.freelist_count(None).unwrap(),
4102            "a fresh database has an empty freelist"
4103        );
4104
4105        conn.batch_execute(PROBE_TABLE).unwrap();
4106        insert_overflowing_row(conn);
4107
4108        crate::delete(pragma_probe::table).execute(conn).unwrap();
4109        assert!(
4110            conn.freelist_count(None).unwrap() > 0,
4111            "deleting the row leaves reclaimable pages on the freelist"
4112        );
4113
4114        // `VACUUM` has no query DSL equivalent.
4115        crate::sql_query("VACUUM").execute(conn).unwrap();
4116        assert_eq!(
4117            0,
4118            conn.freelist_count(None).unwrap(),
4119            "VACUUM reclaims the freelist"
4120        );
4121    }
4122
4123    #[diesel_test_helper::test]
4124    fn schema_targets_the_named_attached_database() {
4125        let conn = &mut connection();
4126        conn.batch_execute(PROBE_TABLE).unwrap();
4127        conn.attach_database(":memory:", "aux").unwrap();
4128        conn.batch_execute(AUX_PROBE_TABLE).unwrap();
4129        crate::insert_into(aux_pragma_probe::table)
4130            .values((
4131                aux_pragma_probe::id.eq(1),
4132                aux_pragma_probe::payload.eq(overflowing_payload()),
4133            ))
4134            .execute(conn)
4135            .unwrap();
4136
4137        let main_pages = conn.page_count(None).unwrap();
4138        let aux_pages = conn.page_count(Some("aux")).unwrap();
4139        assert!(
4140            aux_pages > main_pages,
4141            "the attached database holds the data, main stays small"
4142        );
4143        assert_eq!(
4144            main_pages,
4145            conn.page_count(Some("main")).unwrap(),
4146            "an explicit main matches the default"
4147        );
4148    }
4149
4150    #[diesel_test_helper::test]
4151    fn schema_name_with_backtick_is_escaped() {
4152        // The query builder quotes SQLite identifiers with backticks, so a backtick is
4153        // the character that has to be doubled.
4154        let conn = &mut connection();
4155        let schema = "back`tick";
4156        conn.attach_database(":memory:", schema).unwrap();
4157        conn.batch_execute("CREATE TABLE `back``tick`.probe (id INTEGER PRIMARY KEY)")
4158            .unwrap();
4159
4160        assert!(conn.page_count(Some(schema)).unwrap() > 0);
4161        assert_eq!(0, conn.freelist_count(Some(schema)).unwrap());
4162    }
4163
4164    #[diesel_test_helper::test]
4165    fn unknown_schema_is_reported_as_an_error() {
4166        let conn = &mut connection();
4167
4168        assert!(conn.page_count(Some("nope")).is_err());
4169        assert!(conn.freelist_count(Some("nope")).is_err());
4170    }
4171
4172    // Leaves many pages on the freelist, so `incremental_vacuum` has something to
4173    // reclaim and a bound smaller than the freelist is meaningful.
4174    fn grow_then_empty_freelist(conn: &mut SqliteConnection) {
4175        conn.batch_execute(PROBE_TABLE).unwrap();
4176        let rows = (1..=200)
4177            .map(|id| {
4178                (
4179                    pragma_probe::id.eq(id),
4180                    pragma_probe::payload.eq("x".repeat(4000)),
4181                )
4182            })
4183            .collect::<Vec<_>>();
4184        crate::insert_into(pragma_probe::table)
4185            .values(rows)
4186            .execute(conn)
4187            .unwrap();
4188        crate::delete(pragma_probe::table).execute(conn).unwrap();
4189    }
4190
4191    // The same, in an attached schema.
4192    fn grow_then_empty_aux_freelist(conn: &mut SqliteConnection) {
4193        conn.batch_execute(AUX_PROBE_TABLE).unwrap();
4194        let rows = (1..=200)
4195            .map(|id| {
4196                (
4197                    aux_pragma_probe::id.eq(id),
4198                    aux_pragma_probe::payload.eq("x".repeat(4000)),
4199                )
4200            })
4201            .collect::<Vec<_>>();
4202        crate::insert_into(aux_pragma_probe::table)
4203            .values(rows)
4204            .execute(conn)
4205            .unwrap();
4206        crate::delete(aux_pragma_probe::table)
4207            .execute(conn)
4208            .unwrap();
4209    }
4210
4211    #[diesel_test_helper::test]
4212    fn incremental_vacuum_clears_the_whole_freelist() {
4213        let conn = &mut connection();
4214        conn.set_auto_vacuum(None, AutoVacuumMode::Incremental)
4215            .unwrap();
4216        grow_then_empty_freelist(conn);
4217        assert!(
4218            conn.freelist_count(None).unwrap() > 1,
4219            "the deleted rows should leave many pages on the freelist"
4220        );
4221
4222        conn.incremental_vacuum(None, None).unwrap();
4223
4224        // Stepping the pragma only once would free a single page and leave the rest, so
4225        // this also pins that the statement is driven to completion.
4226        assert_eq!(0, conn.freelist_count(None).unwrap());
4227    }
4228
4229    #[diesel_test_helper::test]
4230    fn incremental_vacuum_reclaims_at_most_the_requested_pages() {
4231        let conn = &mut connection();
4232        conn.set_auto_vacuum(None, AutoVacuumMode::Incremental)
4233            .unwrap();
4234        grow_then_empty_freelist(conn);
4235        let before = conn.freelist_count(None).unwrap();
4236        assert!(before > 10, "the bound has to be smaller than the freelist");
4237
4238        conn.incremental_vacuum(None, Some(10)).unwrap();
4239
4240        let after = conn.freelist_count(None).unwrap();
4241        assert!(after >= before - 10, "at most ten pages may be reclaimed");
4242        assert!(after < before, "some pages should have been reclaimed");
4243    }
4244
4245    #[diesel_test_helper::test]
4246    fn incremental_vacuum_is_a_no_op_outside_incremental_mode() {
4247        let conn = &mut connection();
4248        assert_eq!(AutoVacuumMode::None, conn.auto_vacuum(None).unwrap());
4249        grow_then_empty_freelist(conn);
4250        let before = conn.freelist_count(None).unwrap();
4251        assert!(before > 0);
4252
4253        conn.incremental_vacuum(None, None).unwrap();
4254
4255        assert_eq!(
4256            before,
4257            conn.freelist_count(None).unwrap(),
4258            "a database that is not in incremental mode keeps its freelist"
4259        );
4260    }
4261
4262    #[diesel_test_helper::test]
4263    fn incremental_vacuum_targets_the_named_attached_database() {
4264        let conn = &mut connection();
4265        conn.attach_database(":memory:", "aux").unwrap();
4266        conn.set_auto_vacuum(Some("aux"), AutoVacuumMode::Incremental)
4267            .unwrap();
4268
4269        grow_then_empty_aux_freelist(conn);
4270        assert!(conn.freelist_count(Some("aux")).unwrap() > 0);
4271
4272        conn.incremental_vacuum(Some("aux"), None).unwrap();
4273
4274        assert_eq!(0, conn.freelist_count(Some("aux")).unwrap());
4275    }
4276
4277    #[diesel_test_helper::test]
4278    fn incremental_vacuum_escapes_a_backtick_in_the_schema_name() {
4279        // An unquoted identifier would be a syntax error, and the wrong quoting would
4280        // address a different database.
4281        let conn = &mut connection();
4282        let schema = "back`tick";
4283        conn.attach_database(":memory:", schema).unwrap();
4284
4285        conn.incremental_vacuum(Some(schema), None).unwrap();
4286
4287        assert_eq!(0, conn.freelist_count(Some(schema)).unwrap());
4288    }
4289
4290    #[diesel_test_helper::test]
4291    fn incremental_vacuum_of_zero_pages_clears_everything() {
4292        // SQLite specifies that a bound below one clears the whole freelist.
4293        let conn = &mut connection();
4294        conn.set_auto_vacuum(None, AutoVacuumMode::Incremental)
4295            .unwrap();
4296        grow_then_empty_freelist(conn);
4297        assert!(conn.freelist_count(None).unwrap() > 0);
4298
4299        conn.incremental_vacuum(None, Some(0)).unwrap();
4300
4301        assert_eq!(0, conn.freelist_count(None).unwrap());
4302    }
4303
4304    #[diesel_test_helper::test]
4305    fn incremental_vacuum_of_an_unknown_schema_is_an_error() {
4306        let conn = &mut connection();
4307
4308        assert!(conn.incremental_vacuum(Some("nope"), None).is_err());
4309    }
4310
4311    // Leaves the database holding one small row but occupying many pages, so a rebuild
4312    // has something to reclaim.
4313    fn fill_then_delete(conn: &mut SqliteConnection) {
4314        conn.batch_execute(PROBE_TABLE).unwrap();
4315        crate::insert_into(pragma_probe::table)
4316            .values((
4317                pragma_probe::id.eq(1),
4318                pragma_probe::payload.eq("x".repeat(256 * 1024)),
4319            ))
4320            .execute(conn)
4321            .unwrap();
4322        crate::delete(pragma_probe::table).execute(conn).unwrap();
4323        crate::insert_into(pragma_probe::table)
4324            .values((pragma_probe::id.eq(2), pragma_probe::payload.eq("kept")))
4325            .execute(conn)
4326            .unwrap();
4327    }
4328
4329    // The same, in an attached schema.
4330    fn fill_then_delete_aux(conn: &mut SqliteConnection) {
4331        conn.batch_execute(AUX_PROBE_TABLE).unwrap();
4332        crate::insert_into(aux_pragma_probe::table)
4333            .values((
4334                aux_pragma_probe::id.eq(1),
4335                aux_pragma_probe::payload.eq("x".repeat(256 * 1024)),
4336            ))
4337            .execute(conn)
4338            .unwrap();
4339        crate::delete(aux_pragma_probe::table)
4340            .execute(conn)
4341            .unwrap();
4342    }
4343
4344    #[diesel_test_helper::test]
4345    fn vacuum_repacks_the_database() {
4346        let conn = &mut connection();
4347        fill_then_delete(conn);
4348        let before = conn.page_count(None).unwrap();
4349        assert!(before > 1);
4350
4351        conn.vacuum(None).unwrap();
4352
4353        assert!(
4354            conn.page_count(None).unwrap() < before,
4355            "rebuilding should release the pages the deleted row occupied"
4356        );
4357        assert_eq!(
4358            1,
4359            pragma_probe::table.count().get_result::<i64>(conn).unwrap(),
4360            "the surviving row is still there"
4361        );
4362    }
4363
4364    #[diesel_test_helper::test]
4365    fn vacuum_targets_the_named_attached_database() {
4366        let conn = &mut connection();
4367        conn.attach_database(":memory:", "aux").unwrap();
4368        fill_then_delete_aux(conn);
4369        let before = conn.page_count(Some("aux")).unwrap();
4370        assert!(before > 1);
4371
4372        conn.vacuum(Some("aux")).unwrap();
4373
4374        assert!(conn.page_count(Some("aux")).unwrap() < before);
4375    }
4376
4377    #[diesel_test_helper::test]
4378    fn vacuum_inside_a_transaction_is_an_error() {
4379        use crate::connection::Connection;
4380
4381        let conn = &mut connection();
4382        let result: QueryResult<()> = conn.transaction(|conn| conn.vacuum(None));
4383
4384        assert!(result.is_err());
4385    }
4386
4387    #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
4388    #[diesel_test_helper::test]
4389    fn vacuum_into_writes_a_readable_copy_through_a_quoted_path() {
4390        let dir = tempfile::tempdir().unwrap();
4391        // A quote in the path would break a hand-assembled statement. It is a bind
4392        // parameter, so it is taken verbatim.
4393        let destination = dir.path().join("o'brien backup.db");
4394
4395        let conn = &mut connection();
4396        conn.batch_execute(PROBE_TABLE).unwrap();
4397        crate::insert_into(pragma_probe::table)
4398            .values((pragma_probe::id.eq(1), pragma_probe::payload.eq("copied")))
4399            .execute(conn)
4400            .unwrap();
4401
4402        conn.vacuum_into(None, destination.to_str().unwrap())
4403            .unwrap();
4404
4405        let copy = &mut SqliteConnection::establish(destination.to_str().unwrap()).unwrap();
4406        assert_eq!(
4407            "copied",
4408            pragma_probe::table
4409                .select(pragma_probe::payload)
4410                .get_result::<String>(copy)
4411                .unwrap()
4412        );
4413    }
4414
4415    #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
4416    #[diesel_test_helper::test]
4417    fn vacuum_into_refuses_to_overwrite_an_existing_database() {
4418        let dir = tempfile::tempdir().unwrap();
4419        let destination = dir.path().join("occupied.db");
4420        {
4421            let occupied = &mut SqliteConnection::establish(destination.to_str().unwrap()).unwrap();
4422            occupied.batch_execute(PROBE_TABLE).unwrap();
4423        }
4424
4425        let conn = &mut connection();
4426        conn.batch_execute(PROBE_TABLE).unwrap();
4427
4428        assert!(
4429            conn.vacuum_into(None, destination.to_str().unwrap())
4430                .is_err()
4431        );
4432    }
4433
4434    #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
4435    #[diesel_test_helper::test]
4436    fn vacuum_into_copies_the_named_attached_database() {
4437        let dir = tempfile::tempdir().unwrap();
4438        let destination = dir.path().join("aux copy.db");
4439
4440        let conn = &mut connection();
4441        conn.attach_database(":memory:", "aux").unwrap();
4442        conn.batch_execute(AUX_PROBE_TABLE).unwrap();
4443        crate::insert_into(aux_pragma_probe::table)
4444            .values((
4445                aux_pragma_probe::id.eq(7),
4446                aux_pragma_probe::payload.eq("copied"),
4447            ))
4448            .execute(conn)
4449            .unwrap();
4450
4451        conn.vacuum_into(Some("aux"), destination.to_str().unwrap())
4452            .unwrap();
4453
4454        let copy = &mut SqliteConnection::establish(destination.to_str().unwrap()).unwrap();
4455        // In the copy the table sits in `main`, while `aux_pragma_probe` is declared
4456        // schema-qualified, so this one read cannot go through it.
4457        let id = sql::<Integer>("SELECT id FROM aux_pragma_probe")
4458            .get_result::<i32>(copy)
4459            .unwrap();
4460        assert_eq!(7, id);
4461    }
4462
4463    #[diesel_test_helper::test]
4464    fn vacuum_escapes_a_backtick_in_the_schema_name() {
4465        let conn = &mut connection();
4466        let schema = "back`tick";
4467        conn.attach_database(":memory:", schema).unwrap();
4468
4469        conn.vacuum(Some(schema)).unwrap();
4470    }
4471
4472    #[diesel_test_helper::test]
4473    fn vacuuming_two_schemas_rebuilds_each_of_them() {
4474        // The schema is part of the rendered SQL, so the two calls must not share a
4475        // prepared statement. If they did, the second would rebuild the first's
4476        // database again and leave this one untouched.
4477        let conn = &mut connection();
4478        fill_then_delete(conn);
4479        conn.attach_database(":memory:", "aux").unwrap();
4480        fill_then_delete_aux(conn);
4481
4482        let main_before = conn.page_count(None).unwrap();
4483        let aux_before = conn.page_count(Some("aux")).unwrap();
4484
4485        conn.vacuum(None).unwrap();
4486        conn.vacuum(Some("aux")).unwrap();
4487
4488        assert!(
4489            conn.page_count(None).unwrap() < main_before,
4490            "main was rebuilt"
4491        );
4492        assert!(
4493            conn.page_count(Some("aux")).unwrap() < aux_before,
4494            "aux was rebuilt too, not main a second time"
4495        );
4496    }
4497}