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