Skip to main content

diesel/sqlite/
pragma.rs

1//! Typed access to SQLite [pragmas](https://www.sqlite.org/pragma.html).
2//!
3//! A pragma is declared as a type implementing [`Pragma`], with [`ReadPragma`] when it returns rows and [`WritePragma`] when it can be assigned an argument. Its [`Scope`](Pragma::Scope) decides whether it is used per database or per connection, and the caller picks the Rust type each row decodes into.
4//!
5//! # Per-schema pragmas
6//!
7//! A [`PerSchema`] pragma applies to one database and is read and written through [`SchemaPragma`], where a `None` schema selects `main`.
8//!
9//! ```rust
10//! # include!("../doctest_setup.rs");
11//! use diesel::sqlite::pragma::{PerSchema, Pragma, ReadPragma, SchemaPragma, WritePragma};
12//!
13//! struct UserVersion;
14//!
15//! impl Pragma for UserVersion {
16//!     const NAME: &'static str = "user_version";
17//!     type Scope = PerSchema;
18//! }
19//!
20//! impl ReadPragma for UserVersion {
21//!     type SqlType = Integer;
22//! }
23//!
24//! impl WritePragma for UserVersion {
25//!     type Argument<'a> = i32;
26//! }
27//!
28//! # fn main() -> QueryResult<()> {
29//! let conn = &mut SqliteConnection::establish(":memory:").unwrap();
30//! UserVersion::set(7, None, conn)?;
31//! assert_eq!(7, UserVersion::get::<i32>(None, conn)?);
32//! #     Ok(())
33//! # }
34//! ```
35//!
36//! # Per-connection pragmas
37//!
38//! A [`PerConnection`] pragma applies to the whole connection and is read and written through [`ConnectionPragma`], which takes no schema.
39//!
40//! ```rust
41//! # include!("../doctest_setup.rs");
42//! use diesel::sqlite::pragma::{
43//!     ConnectionPragma, PerConnection, Pragma, ReadPragma, WritePragma,
44//! };
45//!
46//! struct ForeignKeys;
47//!
48//! impl Pragma for ForeignKeys {
49//!     const NAME: &'static str = "foreign_keys";
50//!     type Scope = PerConnection;
51//! }
52//!
53//! impl ReadPragma for ForeignKeys {
54//!     type SqlType = Bool;
55//! }
56//!
57//! impl WritePragma for ForeignKeys {
58//!     type Argument<'a> = bool;
59//! }
60//!
61//! # fn main() -> QueryResult<()> {
62//! let conn = &mut SqliteConnection::establish(":memory:").unwrap();
63//! ForeignKeys::set(true, conn)?;
64//! assert!(ForeignKeys::get::<bool>(conn)?);
65//! #     Ok(())
66//! # }
67//! ```
68
69use crate::connection::{DefaultLoadingMode, LoadConnection};
70use crate::deserialize::FromSqlRow;
71use crate::expression::QueryMetadata;
72use crate::query_builder::{AstPass, Query, QueryFragment, QueryId};
73use crate::result::{Error, QueryResult};
74use crate::sql_types::SqlType;
75use crate::sqlite::{AutoVacuumMode, Sqlite, SqliteConnection};
76use alloc::string::ToString;
77use alloc::vec::Vec;
78use core::marker::PhantomData;
79use core::num::NonZeroU32;
80
81/// A pragma, named by [`NAME`](Self::NAME) and scoped by [`Scope`](Self::Scope).
82pub trait Pragma: Sized {
83    /// The pragma name, written into the SQL unquoted.
84    ///
85    /// A name that is empty or holds anything but ASCII letters, digits and underscores fails with [`Error::QueryBuilderError`] before reaching SQLite. Naming a pragma SQLite knows is up to the implementer, since SQLite ignores unknown ones.
86    const NAME: &'static str;
87    /// [`PerSchema`] or [`PerConnection`].
88    type Scope: PragmaScope;
89}
90
91/// Whether a pragma targets one database or the whole connection.
92pub trait PragmaScope: private::Sealed {}
93
94/// A pragma that applies to one database, `main` or an attached one.
95#[derive(#[automatically_derived]impl ::core::fmt::Debug for PerSchema {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match *self {}
    }
}Debug, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for PerSchema {
}
#[automatically_derived]impl ::core::clone::Clone for PerSchema {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]impl ::core::marker::Copy for PerSchema { }Copy)]
96pub enum PerSchema {}
97
98/// A connection-wide pragma, whose schema qualifier SQLite ignores.
99#[derive(#[automatically_derived]impl ::core::fmt::Debug for PerConnection {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match *self {}
    }
}Debug, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for PerConnection {
}
#[automatically_derived]impl ::core::clone::Clone for PerConnection {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]impl ::core::marker::Copy for PerConnection { }Copy)]
100pub enum PerConnection {}
101
102impl PragmaScope for PerSchema {}
103impl PragmaScope for PerConnection {}
104
105mod private {
106    use crate::query_builder::AstPass;
107    use crate::result::QueryResult;
108    use crate::sqlite::Sqlite;
109
110    pub trait Sealed {}
111    impl Sealed for super::PerSchema {}
112    impl Sealed for super::PerConnection {}
113
114    pub trait RenderArgument {
115        fn render(&self, out: &mut AstPass<'_, '_, Sqlite>) -> QueryResult<()>;
116    }
117}
118
119/// A pragma that returns rows when queried.
120pub trait ReadPragma: Pragma {
121    /// The SQL type of one returned row.
122    type SqlType: SqlType;
123}
124
125/// A pragma that can be assigned an [`Argument`](Self::Argument).
126///
127/// Never assign secrets this way, since [`Instrumentation`](crate::connection::Instrumentation), [`on_trace`](SqliteConnection::on_trace) and [`on_authorize`](SqliteConnection::on_authorize) observe the statement.
128pub trait WritePragma: Pragma {
129    /// The argument assigned to the pragma, accepted as any type converting into it.
130    type Argument<'a>: PragmaArgument;
131}
132
133/// A value written into a pragma statement, which takes no bind parameters.
134///
135/// Diesel renders every argument itself, so this trait cannot be implemented outside Diesel. A type of your own becomes an argument by converting into one with [`From`].
136///
137/// Integers and `bool` are written as decimal integers. An `Option<NonZeroU32>` writes `None` as 0, which limit pragmas such as `incremental_vacuum` read as no limit.
138pub trait PragmaArgument: private::RenderArgument {}
139
140/// A keyword written unquoted, such as `WAL` or `NORMAL`.
141///
142/// A keyword that is empty or holds anything but ASCII letters, digits and underscores fails with [`Error::QueryBuilderError`] before reaching SQLite.
143///
144/// ```rust
145/// # include!("../doctest_setup.rs");
146/// use diesel::sqlite::pragma::{
147///     PerSchema, Pragma, PragmaKeyword, ReadPragma, SchemaPragma, WritePragma,
148/// };
149///
150/// struct Synchronous;
151///
152/// impl Pragma for Synchronous {
153///     const NAME: &'static str = "synchronous";
154///     type Scope = PerSchema;
155/// }
156///
157/// impl ReadPragma for Synchronous {
158///     type SqlType = Integer;
159/// }
160///
161/// impl WritePragma for Synchronous {
162///     type Argument<'a> = PragmaKeyword;
163/// }
164///
165/// enum SynchronousMode {
166///     Off,
167///     Normal,
168/// }
169///
170/// impl From<SynchronousMode> for PragmaKeyword {
171///     fn from(mode: SynchronousMode) -> Self {
172///         match mode {
173///             SynchronousMode::Off => PragmaKeyword::new("OFF"),
174///             SynchronousMode::Normal => PragmaKeyword::new("NORMAL"),
175///         }
176///     }
177/// }
178///
179/// # fn main() -> QueryResult<()> {
180/// let conn = &mut SqliteConnection::establish(":memory:").unwrap();
181/// Synchronous::set(SynchronousMode::Normal, None, conn)?;
182/// assert_eq!(1, Synchronous::get::<i32>(None, conn)?);
183/// Synchronous::set(SynchronousMode::Off, None, conn)?;
184/// assert_eq!(0, Synchronous::get::<i32>(None, conn)?);
185/// #     Ok(())
186/// # }
187/// ```
188#[derive(#[automatically_derived]impl ::core::fmt::Debug for PragmaKeyword {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "PragmaKeyword",
            &&self.0)
    }
}Debug, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for PragmaKeyword {
}
#[automatically_derived]impl ::core::clone::Clone for PragmaKeyword {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<&'static str>;
        *self
    }
}Clone, #[automatically_derived]impl ::core::marker::Copy for PragmaKeyword { }Copy, #[automatically_derived]impl ::core::marker::StructuralPartialEq for
    PragmaKeyword {
}
#[automatically_derived]impl ::core::cmp::PartialEq for PragmaKeyword {
    #[inline]
    fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]impl ::core::cmp::Eq for PragmaKeyword {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<&'static str>;
    }
}Eq)]
189pub struct PragmaKeyword(&'static str);
190
191impl PragmaKeyword {
192    /// Wrap `keyword`, which is checked when the statement is built.
193    pub const fn new(keyword: &'static str) -> Self {
194        Self(keyword)
195    }
196}
197
198impl private::RenderArgument for PragmaKeyword {
199    fn render(&self, out: &mut AstPass<'_, '_, Sqlite>) -> QueryResult<()> {
200        push_bare_word(out, "keyword", self.0)
201    }
202}
203
204impl PragmaArgument for PragmaKeyword {}
205
206/// Text written as a single-quoted SQL string with embedded quotes doubled, such as `'UTF-16le'`.
207///
208/// Text holding a NUL byte fails with [`Error::InvalidCString`] before reaching SQLite.
209///
210/// ```rust
211/// # include!("../doctest_setup.rs");
212/// use diesel::sqlite::pragma::{
213///     ConnectionPragma, PerConnection, Pragma, PragmaText, ReadPragma, WritePragma,
214/// };
215///
216/// struct Encoding;
217///
218/// impl Pragma for Encoding {
219///     const NAME: &'static str = "encoding";
220///     type Scope = PerConnection;
221/// }
222///
223/// impl ReadPragma for Encoding {
224///     type SqlType = Text;
225/// }
226///
227/// impl WritePragma for Encoding {
228///     type Argument<'a> = PragmaText<'a>;
229/// }
230///
231/// # // Reading text allocated by the native sqlite library is not supported
232/// # // by miri `-Zmiri-native-lib`.
233/// # #[cfg(not(miri))]
234/// # fn main() -> QueryResult<()> {
235/// let conn = &mut SqliteConnection::establish(":memory:").unwrap();
236/// // SQLite only changes the encoding before anything is written.
237/// Encoding::set("UTF-16le", conn)?;
238/// assert_eq!("UTF-16le", Encoding::get::<String>(conn)?);
239/// #     Ok(())
240/// # }
241/// # #[cfg(miri)]
242/// # fn main() {}
243/// ```
244#[derive(#[automatically_derived]impl<'a> ::core::fmt::Debug for PragmaText<'a> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "PragmaText",
            &&self.0)
    }
}Debug, #[automatically_derived]#[doc(hidden)]unsafe impl<'a>
    ::core::clone::TrivialClone for PragmaText<'a> {
}
#[automatically_derived]impl<'a> ::core::clone::Clone for PragmaText<'a> {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<&'a str>;
        *self
    }
}Clone, #[automatically_derived]impl<'a> ::core::marker::Copy for PragmaText<'a> { }Copy, #[automatically_derived]impl<'a> ::core::marker::StructuralPartialEq for
    PragmaText<'a> {
}
#[automatically_derived]impl<'a> ::core::cmp::PartialEq for PragmaText<'a> {
    #[inline]
    fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]impl<'a> ::core::cmp::Eq for PragmaText<'a> {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<&'a str>;
    }
}Eq)]
245pub struct PragmaText<'a>(&'a str);
246
247impl<'a> From<&'a str> for PragmaText<'a> {
248    fn from(text: &'a str) -> Self {
249        Self(text)
250    }
251}
252
253impl private::RenderArgument for PragmaText<'_> {
254    fn render(&self, out: &mut AstPass<'_, '_, Sqlite>) -> QueryResult<()> {
255        out.push_sql("'");
256        for piece in self.0.split_inclusive('\'') {
257            out.push_sql(piece);
258            if piece.ends_with('\'') {
259                out.push_sql("'");
260            }
261        }
262        out.push_sql("'");
263        Ok(())
264    }
265}
266
267impl PragmaArgument for PragmaText<'_> {}
268
269macro_rules! integer_pragma_argument {
270    ($($ty:ty),*) => {$(
271        impl private::RenderArgument for $ty {
272            fn render(&self, out: &mut AstPass<'_, '_, Sqlite>) -> QueryResult<()> {
273                out.push_sql(&i64::from(*self).to_string());
274                Ok(())
275            }
276        }
277
278        impl PragmaArgument for $ty {}
279    )*};
280}
281
282impl private::RenderArgument for i8 {
    fn render(&self, out: &mut AstPass<'_, '_, Sqlite>) -> QueryResult<()> {
        out.push_sql(&i64::from(*self).to_string());
        Ok(())
    }
}
impl PragmaArgument for i8 {}
impl private::RenderArgument for i16 {
    fn render(&self, out: &mut AstPass<'_, '_, Sqlite>) -> QueryResult<()> {
        out.push_sql(&i64::from(*self).to_string());
        Ok(())
    }
}
impl PragmaArgument for i16 {}
impl private::RenderArgument for i32 {
    fn render(&self, out: &mut AstPass<'_, '_, Sqlite>) -> QueryResult<()> {
        out.push_sql(&i64::from(*self).to_string());
        Ok(())
    }
}
impl PragmaArgument for i32 {}
impl private::RenderArgument for i64 {
    fn render(&self, out: &mut AstPass<'_, '_, Sqlite>) -> QueryResult<()> {
        out.push_sql(&i64::from(*self).to_string());
        Ok(())
    }
}
impl PragmaArgument for i64 {}
impl private::RenderArgument for u8 {
    fn render(&self, out: &mut AstPass<'_, '_, Sqlite>) -> QueryResult<()> {
        out.push_sql(&i64::from(*self).to_string());
        Ok(())
    }
}
impl PragmaArgument for u8 {}
impl private::RenderArgument for u16 {
    fn render(&self, out: &mut AstPass<'_, '_, Sqlite>) -> QueryResult<()> {
        out.push_sql(&i64::from(*self).to_string());
        Ok(())
    }
}
impl PragmaArgument for u16 {}
impl private::RenderArgument for u32 {
    fn render(&self, out: &mut AstPass<'_, '_, Sqlite>) -> QueryResult<()> {
        out.push_sql(&i64::from(*self).to_string());
        Ok(())
    }
}
impl PragmaArgument for u32 {}
impl private::RenderArgument for bool {
    fn render(&self, out: &mut AstPass<'_, '_, Sqlite>) -> QueryResult<()> {
        out.push_sql(&i64::from(*self).to_string());
        Ok(())
    }
}
impl PragmaArgument for bool {}integer_pragma_argument!(i8, i16, i32, i64, u8, u16, u32, bool);
283
284/// Reads and writes a [`PerSchema`] pragma, where a `None` schema selects `main`.
285pub trait SchemaPragma: Pragma<Scope = PerSchema> {
286    /// Read the first row, returning [`Error::NotFound`] if the pragma returns none.
287    fn get<Row>(schema: Option<&str>, conn: &mut SqliteConnection) -> QueryResult<Row>
288    where
289        Self: ReadPragma,
290        Sqlite: QueryMetadata<Self::SqlType>,
291        Row: FromSqlRow<Self::SqlType, Sqlite>,
292    {
293        first_row::<Self, Row>(conn, Some(schema.unwrap_or("main")))
294    }
295
296    /// Read every row.
297    fn get_all<Row>(schema: Option<&str>, conn: &mut SqliteConnection) -> QueryResult<Vec<Row>>
298    where
299        Self: ReadPragma,
300        Sqlite: QueryMetadata<Self::SqlType>,
301        Row: FromSqlRow<Self::SqlType, Sqlite>,
302    {
303        all_rows::<Self, Row>(conn, Some(schema.unwrap_or("main")))
304    }
305
306    /// Assign `argument`, running the statement to completion without confirming the value SQLite applied.
307    fn set<'a>(
308        argument: impl Into<Self::Argument<'a>>,
309        schema: Option<&str>,
310        conn: &mut SqliteConnection,
311    ) -> QueryResult<()>
312    where
313        Self: WritePragma,
314    {
315        assign::<Self>(conn, Some(schema.unwrap_or("main")), argument.into())
316    }
317}
318
319impl<P: Pragma<Scope = PerSchema>> SchemaPragma for P {}
320
321/// Reads and writes a [`PerConnection`] pragma.
322pub trait ConnectionPragma: Pragma<Scope = PerConnection> {
323    /// Read the first row, returning [`Error::NotFound`] if the pragma returns none.
324    fn get<Row>(conn: &mut SqliteConnection) -> QueryResult<Row>
325    where
326        Self: ReadPragma,
327        Sqlite: QueryMetadata<Self::SqlType>,
328        Row: FromSqlRow<Self::SqlType, Sqlite>,
329    {
330        first_row::<Self, Row>(conn, None)
331    }
332
333    /// Read every row.
334    fn get_all<Row>(conn: &mut SqliteConnection) -> QueryResult<Vec<Row>>
335    where
336        Self: ReadPragma,
337        Sqlite: QueryMetadata<Self::SqlType>,
338        Row: FromSqlRow<Self::SqlType, Sqlite>,
339    {
340        all_rows::<Self, Row>(conn, None)
341    }
342
343    /// Assign `argument`, running the statement to completion without confirming the value SQLite applied.
344    fn set<'a>(
345        argument: impl Into<Self::Argument<'a>>,
346        conn: &mut SqliteConnection,
347    ) -> QueryResult<()>
348    where
349        Self: WritePragma,
350    {
351        assign::<Self>(conn, None, argument.into())
352    }
353}
354
355impl<P: Pragma<Scope = PerConnection>> ConnectionPragma for P {}
356
357/// `PRAGMA page_count`, the total number of pages in a database, read as an `i64`.
358///
359/// Multiply by the page size for the size the database accounts for.
360///
361/// ```rust
362/// # include!("../doctest_setup.rs");
363/// use diesel::connection::SimpleConnection;
364/// use diesel::sqlite::pragma::{PageCount, SchemaPragma};
365///
366/// # fn main() -> QueryResult<()> {
367/// let conn = &mut SqliteConnection::establish(":memory:").unwrap();
368/// // An empty database occupies no pages until something is written.
369/// assert_eq!(0, PageCount::get::<i64>(None, conn)?);
370/// conn.batch_execute("CREATE TABLE items (id INTEGER PRIMARY KEY)")?;
371/// assert!(PageCount::get::<i64>(None, conn)? > 0);
372/// #     Ok(())
373/// # }
374/// ```
375#[derive(#[automatically_derived]impl ::core::fmt::Debug for PageCount {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "PageCount")
    }
}Debug, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for PageCount {
}
#[automatically_derived]impl ::core::clone::Clone for PageCount {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]impl ::core::marker::Copy for PageCount { }Copy)]
376pub struct PageCount;
377
378impl Pragma for PageCount {
379    const NAME: &'static str = "page_count";
380    type Scope = PerSchema;
381}
382
383impl ReadPragma for PageCount {
384    type SqlType = crate::sql_types::BigInt;
385}
386
387/// `PRAGMA freelist_count`, the unused pages on a database's freelist, read as an `i64`.
388///
389/// A growing freelist is reclaimable space, freed by `VACUUM`.
390///
391/// ```rust
392/// # include!("../doctest_setup.rs");
393/// use diesel::sqlite::pragma::{FreelistCount, SchemaPragma};
394///
395/// # fn main() -> QueryResult<()> {
396/// let conn = &mut SqliteConnection::establish(":memory:").unwrap();
397/// assert_eq!(0, FreelistCount::get::<i64>(None, conn)?);
398/// #     Ok(())
399/// # }
400/// ```
401#[derive(#[automatically_derived]impl ::core::fmt::Debug for FreelistCount {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "FreelistCount")
    }
}Debug, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for FreelistCount {
}
#[automatically_derived]impl ::core::clone::Clone for FreelistCount {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]impl ::core::marker::Copy for FreelistCount { }Copy)]
402pub struct FreelistCount;
403
404impl Pragma for FreelistCount {
405    const NAME: &'static str = "freelist_count";
406    type Scope = PerSchema;
407}
408
409impl ReadPragma for FreelistCount {
410    type SqlType = crate::sql_types::BigInt;
411}
412
413/// `PRAGMA auto_vacuum`, read and set as an [`AutoVacuumMode`].
414///
415/// Changing from or to [`AutoVacuumMode::None`] only takes effect on a database with no tables yet, or after a subsequent `VACUUM`.
416///
417/// ```rust
418/// # include!("../doctest_setup.rs");
419/// use diesel::sqlite::AutoVacuumMode;
420/// use diesel::sqlite::pragma::{AutoVacuum, SchemaPragma};
421///
422/// # fn main() -> QueryResult<()> {
423/// let conn = &mut SqliteConnection::establish(":memory:").unwrap();
424/// assert_eq!(AutoVacuumMode::None, AutoVacuum::get::<AutoVacuumMode>(None, conn)?);
425/// AutoVacuum::set(AutoVacuumMode::Incremental, None, conn)?;
426/// assert_eq!(
427///     AutoVacuumMode::Incremental,
428///     AutoVacuum::get::<AutoVacuumMode>(None, conn)?
429/// );
430/// #     Ok(())
431/// # }
432/// ```
433#[derive(#[automatically_derived]impl ::core::fmt::Debug for AutoVacuum {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "AutoVacuum")
    }
}Debug, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for AutoVacuum {
}
#[automatically_derived]impl ::core::clone::Clone for AutoVacuum {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]impl ::core::marker::Copy for AutoVacuum { }Copy)]
434pub struct AutoVacuum;
435
436impl Pragma for AutoVacuum {
437    const NAME: &'static str = "auto_vacuum";
438    type Scope = PerSchema;
439}
440
441impl ReadPragma for AutoVacuum {
442    type SqlType = crate::sql_types::Integer;
443}
444
445impl WritePragma for AutoVacuum {
446    type Argument<'a> = AutoVacuumMode;
447}
448
449impl private::RenderArgument for AutoVacuumMode {
450    fn render(&self, out: &mut AstPass<'_, '_, Sqlite>) -> QueryResult<()> {
451        // Discriminant extraction, `#[repr(i32)]` makes it the mode's SQLite number.
452        out.push_sql(&(*self as i32).to_string());
453        Ok(())
454    }
455}
456
457impl PragmaArgument for AutoVacuumMode {}
458
459/// `PRAGMA incremental_vacuum`, shrinking a database by releasing freelist pages without the full rewrite [`VACUUM`](https://www.sqlite.org/lang_vacuum.html) performs.
460///
461/// `Some(pages)` releases at most that many pages, `None` the whole freelist. Only a database in [`AutoVacuumMode::Incremental`] has anything to release, on any other mode the assignment succeeds and does nothing.
462///
463/// ```rust
464/// # include!("../doctest_setup.rs");
465/// use diesel::sqlite::AutoVacuumMode;
466/// use diesel::sqlite::pragma::{AutoVacuum, IncrementalVacuum, SchemaPragma};
467/// use std::num::NonZeroU32;
468///
469/// # fn main() -> QueryResult<()> {
470/// let conn = &mut SqliteConnection::establish(":memory:").unwrap();
471/// AutoVacuum::set(AutoVacuumMode::Incremental, None, conn)?;
472/// // Release at most 8 pages, then whatever is left.
473/// IncrementalVacuum::set(NonZeroU32::new(8), None, conn)?;
474/// IncrementalVacuum::set(None, None, conn)?;
475/// #     Ok(())
476/// # }
477/// ```
478#[derive(#[automatically_derived]impl ::core::fmt::Debug for IncrementalVacuum {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "IncrementalVacuum")
    }
}Debug, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for IncrementalVacuum {
}
#[automatically_derived]impl ::core::clone::Clone for IncrementalVacuum {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]impl ::core::marker::Copy for IncrementalVacuum { }Copy)]
479pub struct IncrementalVacuum;
480
481impl Pragma for IncrementalVacuum {
482    const NAME: &'static str = "incremental_vacuum";
483    type Scope = PerSchema;
484}
485
486impl WritePragma for IncrementalVacuum {
487    type Argument<'a> = Option<NonZeroU32>;
488}
489
490impl private::RenderArgument for Option<NonZeroU32> {
491    fn render(&self, out: &mut AstPass<'_, '_, Sqlite>) -> QueryResult<()> {
492        out.push_sql(&self.map_or(0, NonZeroU32::get).to_string());
493        Ok(())
494    }
495}
496
497impl PragmaArgument for Option<NonZeroU32> {}
498
499fn cursor<'conn, 'query, P>(
500    conn: &'conn mut SqliteConnection,
501    schema: Option<&'query str>,
502) -> QueryResult<<SqliteConnection as LoadConnection<DefaultLoadingMode>>::Cursor<'conn, 'query>>
503where
504    P: ReadPragma + 'query,
505    Sqlite: QueryMetadata<P::SqlType>,
506{
507    LoadConnection::<DefaultLoadingMode>::load(conn, PragmaQuery::<P>::new(schema))
508}
509
510fn first_row<P, Row>(conn: &mut SqliteConnection, schema: Option<&str>) -> QueryResult<Row>
511where
512    P: ReadPragma,
513    Sqlite: QueryMetadata<P::SqlType>,
514    Row: FromSqlRow<P::SqlType, Sqlite>,
515{
516    let mut cursor = cursor::<P>(conn, schema)?;
517    let row = cursor.next().ok_or(Error::NotFound)??;
518    Row::build_from_row(&row).map_err(Error::DeserializationError)
519}
520
521fn all_rows<P, Row>(conn: &mut SqliteConnection, schema: Option<&str>) -> QueryResult<Vec<Row>>
522where
523    P: ReadPragma,
524    Sqlite: QueryMetadata<P::SqlType>,
525    Row: FromSqlRow<P::SqlType, Sqlite>,
526{
527    cursor::<P>(conn, schema)?
528        .map(|row| Row::build_from_row(&row?).map_err(Error::DeserializationError))
529        .collect()
530}
531
532fn assign<P: WritePragma>(
533    conn: &mut SqliteConnection,
534    schema: Option<&str>,
535    argument: P::Argument<'_>,
536) -> QueryResult<()> {
537    use crate::connection::SimpleConnection;
538    use crate::query_builder::QueryBuilder;
539    use crate::sqlite::SqliteQueryBuilder;
540
541    // Some pragmas do one unit of work per step, `incremental_vacuum` frees one page per step.
542    // `sqlite3_exec` behind `batch_execute` runs to completion, while `StatementUse::run` steps once.
543    let mut query = SqliteQueryBuilder::new();
544    PragmaAssignment::<P, _> {
545        schema,
546        argument,
547        pragma: PhantomData,
548    }
549    .to_sql(&mut query, &Sqlite)?;
550    conn.batch_execute(&query.finish())
551}
552
553#[derive(#[automatically_derived]impl<'a, P: ::core::fmt::Debug> ::core::fmt::Debug for
    PragmaQuery<'a, P> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f,
            "PragmaQuery schema pragma", &self.schema, &&self.pragma)
    }
}Debug)]
554struct PragmaQuery<'a, P> {
555    schema: Option<&'a str>,
556
557    pragma: PhantomData<P>,
558}
559
560impl<'a, P: Pragma> PragmaQuery<'a, P> {
561    fn new(schema: Option<&'a str>) -> Self {
562        Self {
563            schema,
564            pragma: PhantomData,
565        }
566    }
567}
568
569impl<P: Pragma> QueryFragment<Sqlite> for PragmaQuery<'_, P> {
570    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> {
571        push_pragma::<P>(&mut out, self.schema)
572    }
573}
574
575// The schema name is runtime data, so the rendered SQL is not determined by the type.
576impl<P> QueryId for PragmaQuery<'_, P> {
577    type QueryId = ();
578
579    const HAS_STATIC_QUERY_ID: bool = false;
580}
581
582impl<P: ReadPragma> Query for PragmaQuery<'_, P> {
583    type SqlType = P::SqlType;
584}
585
586struct PragmaAssignment<'a, P, A> {
587    schema: Option<&'a str>,
588    argument: A,
589    pragma: PhantomData<P>,
590}
591
592impl<P: Pragma, A: PragmaArgument> QueryFragment<Sqlite> for PragmaAssignment<'_, P, A> {
593    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> {
594        push_pragma::<P>(&mut out, self.schema)?;
595        out.push_sql(" = ");
596        self.argument.render(&mut out)
597    }
598}
599
600fn push_pragma<P: Pragma>(
601    out: &mut AstPass<'_, '_, Sqlite>,
602    schema: Option<&str>,
603) -> QueryResult<()> {
604    out.push_sql("PRAGMA ");
605    if let Some(schema) = schema {
606        out.push_identifier(schema)?;
607        out.push_sql(".");
608    }
609    push_bare_word(out, "name", P::NAME)
610}
611
612fn push_bare_word(
613    out: &mut AstPass<'_, '_, Sqlite>,
614    kind: &str,
615    word: &'static str,
616) -> QueryResult<()> {
617    if word.is_empty()
618        || !word
619            .bytes()
620            .all(|byte| byte.is_ascii_alphanumeric() || byte == b'_')
621    {
622        return Err(Error::QueryBuilderError(
623            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}` is not a valid pragma {1}",
                word, kind))
    })alloc::format!("`{word}` is not a valid pragma {kind}").into(),
624        ));
625    }
626    out.push_sql(word);
627    Ok(())
628}