sqlx_core_oldapi/sqlite/options/
mod.rs

1use std::path::Path;
2
3mod auto_vacuum;
4mod connect;
5mod journal_mode;
6mod locking_mode;
7mod parse;
8mod synchronous;
9
10use crate::connection::LogSettings;
11pub use auto_vacuum::SqliteAutoVacuum;
12pub use journal_mode::SqliteJournalMode;
13pub use locking_mode::SqliteLockingMode;
14use std::cmp::Ordering;
15use std::sync::Arc;
16use std::{borrow::Cow, time::Duration};
17pub use synchronous::SqliteSynchronous;
18
19use crate::common::DebugFn;
20use crate::sqlite::connection::collation::Collation;
21use crate::sqlite::connection::function::Function;
22use indexmap::IndexMap;
23
24/// Options and flags which can be used to configure a SQLite connection.
25///
26/// A value of `SqliteConnectOptions` can be parsed from a connection URL,
27/// as described by [SQLite](https://www.sqlite.org/uri.html).
28///
29/// | URL | Description |
30/// | -- | -- |
31/// `sqlite::memory:` | Open an in-memory database. |
32/// `sqlite:data.db` | Open the file `data.db` in the current directory. |
33/// `sqlite://data.db` | Open the file `data.db` in the current directory. |
34/// `sqlite:///data.db` | Open the file `data.db` from the root (`/`) directory. |
35/// `sqlite://data.db?mode=ro` | Open the file `data.db` for read-only access. |
36///
37/// # Example
38///
39/// ```rust,no_run
40/// # use sqlx_core_oldapi::connection::ConnectOptions;
41/// # use sqlx_core_oldapi::error::Error;
42/// use sqlx::sqlite::{SqliteConnectOptions, SqliteJournalMode};
43/// use std::str::FromStr;
44///
45/// # fn main() {
46/// # #[cfg(feature = "_rt-async-std")]
47/// # sqlx_rt::async_std::task::block_on::<_, Result<(), Error>>(async move {
48/// let conn = SqliteConnectOptions::from_str("sqlite://data.db")?
49///     .journal_mode(SqliteJournalMode::Wal)
50///     .read_only(true)
51///     .connect().await?;
52/// # Ok(())
53/// # }).unwrap();
54/// # }
55/// ```
56#[derive(Clone, Debug)]
57pub struct SqliteConnectOptions {
58    pub(crate) filename: Cow<'static, Path>,
59    pub(crate) in_memory: bool,
60    pub(crate) read_only: bool,
61    pub(crate) create_if_missing: bool,
62    pub(crate) shared_cache: bool,
63    pub(crate) statement_cache_capacity: usize,
64    pub(crate) busy_timeout: Duration,
65    pub(crate) log_settings: LogSettings,
66    pub(crate) immutable: bool,
67    pub(crate) vfs: Option<Cow<'static, str>>,
68
69    pub(crate) pragmas: IndexMap<Cow<'static, str>, Option<Cow<'static, str>>>,
70    /// Extensions are specified as a pair of <Extension Name : Optional Entry Point>, the majority
71    /// of SQLite extensions will use the default entry points specified in the docs, these should
72    /// be added to the map with a `None` value.
73    /// <https://www.sqlite.org/loadext.html#loading_an_extension>
74    pub(crate) extensions: IndexMap<Cow<'static, str>, Option<Cow<'static, str>>>,
75
76    pub(crate) command_channel_size: usize,
77    pub(crate) row_channel_size: usize,
78
79    pub(crate) collations: Vec<Collation>,
80    pub(crate) functions: Vec<Function>,
81
82    pub(crate) serialized: bool,
83    pub(crate) thread_name: Arc<DebugFn<dyn Fn(u64) -> String + Send + Sync + 'static>>,
84}
85
86impl Default for SqliteConnectOptions {
87    fn default() -> Self {
88        Self::new()
89    }
90}
91
92impl SqliteConnectOptions {
93    /// Construct `Self` with default options.
94    ///
95    /// See the source of this method for the current defaults.
96    pub fn new() -> Self {
97        let mut pragmas: IndexMap<Cow<'static, str>, Option<Cow<'static, str>>> = IndexMap::new();
98
99        // Standard pragmas
100        //
101        // Most of these don't actually need to be sent because they would be set to their
102        // default values anyway. See the SQLite documentation for default values of these PRAGMAs:
103        // https://www.sqlite.org/pragma.html
104        //
105        // However, by inserting into the map here, we can ensure that they're set in the proper
106        // order, even if they're overwritten later by their respective setters or
107        // directly by `pragma()`
108
109        // SQLCipher special case: if the `key` pragma is set, it must be executed first.
110        pragmas.insert("key".into(), None);
111
112        // Other SQLCipher pragmas that has to be after the key, but before any other operation on the database.
113        // https://www.zetetic.net/sqlcipher/sqlcipher-api/
114
115        // Bytes of the database file that is not encrypted
116        // Default for SQLCipher v4 is 0
117        // If greater than zero 'cipher_salt' pragma must be also defined
118        pragmas.insert("cipher_plaintext_header_size".into(), None);
119
120        // Allows to provide salt manually
121        // By default SQLCipher sets salt automatically, use only in conjunction with
122        // 'cipher_plaintext_header_size' pragma
123        pragmas.insert("cipher_salt".into(), None);
124
125        // Number of iterations used in PBKDF2 key derivation.
126        // Default for SQLCipher v4 is 256000
127        pragmas.insert("kdf_iter".into(), None);
128
129        // Define KDF algorithm to be used.
130        // Default for SQLCipher v4 is PBKDF2_HMAC_SHA512.
131        pragmas.insert("cipher_kdf_algorithm".into(), None);
132
133        // Enable or disable HMAC functionality.
134        // Default for SQLCipher v4 is 1.
135        pragmas.insert("cipher_use_hmac".into(), None);
136
137        // Set default encryption settings depending on the version 1,2,3, or 4.
138        pragmas.insert("cipher_compatibility".into(), None);
139
140        // Page size of encrypted database.
141        // Default for SQLCipher v4 is 4096.
142        pragmas.insert("cipher_page_size".into(), None);
143
144        // Choose algorithm used for HMAC.
145        // Default for SQLCipher v4 is HMAC_SHA512.
146        pragmas.insert("cipher_hmac_algorithm".into(), None);
147
148        // Normally, page_size must be set before any other action on the database.
149        // Defaults to 4096 for new databases.
150        pragmas.insert("page_size".into(), None);
151
152        // locking_mode should be set before journal_mode:
153        // https://www.sqlite.org/wal.html#use_of_wal_without_shared_memory
154        pragmas.insert("locking_mode".into(), None);
155
156        // Don't set `journal_mode` unless the user requested it.
157        // WAL mode is a permanent setting for created databases and changing into or out of it
158        // requires an exclusive lock that can't be waited on with `sqlite3_busy_timeout()`.
159        // https://github.com/launchbadge/sqlx/pull/1930#issuecomment-1168165414
160        pragmas.insert("journal_mode".into(), None);
161
162        // We choose to enable foreign key enforcement by default, though SQLite normally
163        // leaves it off for backward compatibility: https://www.sqlite.org/foreignkeys.html#fk_enable
164        pragmas.insert("foreign_keys".into(), Some("ON".into()));
165
166        // The `synchronous` pragma defaults to FULL
167        // https://www.sqlite.org/compile.html#default_synchronous.
168        pragmas.insert("synchronous".into(), None);
169
170        pragmas.insert("auto_vacuum".into(), None);
171
172        Self {
173            filename: Cow::Borrowed(Path::new(":memory:")),
174            in_memory: false,
175            read_only: false,
176            create_if_missing: false,
177            shared_cache: false,
178            statement_cache_capacity: 100,
179            busy_timeout: Duration::from_secs(5),
180            log_settings: Default::default(),
181            immutable: false,
182            vfs: None,
183            pragmas,
184            extensions: Default::default(),
185            collations: Default::default(),
186            functions: Default::default(),
187            serialized: false,
188            thread_name: Arc::new(DebugFn(|id| format!("sqlx-sqlite-worker-{}", id))),
189            command_channel_size: 50,
190            row_channel_size: 50,
191        }
192    }
193
194    /// Sets the name of the database file.
195    pub fn filename(mut self, filename: impl AsRef<Path>) -> Self {
196        self.filename = Cow::Owned(filename.as_ref().to_owned());
197        self
198    }
199
200    /// Set the enforcement of [foreign key constraints](https://www.sqlite.org/pragma.html#pragma_foreign_keys).
201    ///
202    /// SQLx chooses to enable this by default so that foreign keys function as expected,
203    /// compared to other database flavors.
204    pub fn foreign_keys(self, on: bool) -> Self {
205        self.pragma("foreign_keys", if on { "ON" } else { "OFF" })
206    }
207
208    /// Set the [`SQLITE_OPEN_SHAREDCACHE` flag](https://sqlite.org/sharedcache.html).
209    ///
210    /// By default, this is disabled.
211    pub fn shared_cache(mut self, on: bool) -> Self {
212        self.shared_cache = on;
213        self
214    }
215
216    /// Sets the [journal mode](https://www.sqlite.org/pragma.html#pragma_journal_mode) for the database connection.
217    ///
218    /// Journal modes are ephemeral per connection, with the exception of the
219    /// [Write-Ahead Log (WAL) mode](https://www.sqlite.org/wal.html).
220    ///
221    /// A database created in WAL mode retains the setting and will apply it to all connections
222    /// opened against it that don't set a `journal_mode`.
223    ///
224    /// Opening a connection to a database created in WAL mode with a different `journal_mode` will
225    /// erase the setting on the database, requiring an exclusive lock to do so.
226    /// You may get a `database is locked` (corresponding to `SQLITE_BUSY`) error if another
227    /// connection is accessing the database file at the same time.
228    ///
229    /// SQLx does not set a journal mode by default, to avoid unintentionally changing a database
230    /// into or out of WAL mode.
231    ///
232    /// The default journal mode for non-WAL databases is `DELETE`, or `MEMORY` for in-memory
233    /// databases.
234    ///
235    /// For consistency, any commands in `sqlx-cli` which create a SQLite database will create it
236    /// in WAL mode.
237    pub fn journal_mode(self, mode: SqliteJournalMode) -> Self {
238        self.pragma("journal_mode", mode.as_str())
239    }
240
241    /// Sets the [locking mode](https://www.sqlite.org/pragma.html#pragma_locking_mode) for the database connection.
242    ///
243    /// The default locking mode is NORMAL.
244    pub fn locking_mode(self, mode: SqliteLockingMode) -> Self {
245        self.pragma("locking_mode", mode.as_str())
246    }
247
248    /// Sets the [access mode](https://www.sqlite.org/c3ref/open.html) to open the database
249    /// for read-only access.
250    pub fn read_only(mut self, read_only: bool) -> Self {
251        self.read_only = read_only;
252        self
253    }
254
255    /// Sets the [access mode](https://www.sqlite.org/c3ref/open.html) to create the database file
256    /// if the file does not exist.
257    ///
258    /// By default, a new file **will not be created** if one is not found.
259    pub fn create_if_missing(mut self, create: bool) -> Self {
260        self.create_if_missing = create;
261        self
262    }
263
264    /// Sets the capacity of the connection's statement cache in a number of stored
265    /// distinct statements. Caching is handled using LRU, meaning when the
266    /// amount of queries hits the defined limit, the oldest statement will get
267    /// dropped.
268    ///
269    /// The default cache capacity is 100 statements.
270    pub fn statement_cache_capacity(mut self, capacity: usize) -> Self {
271        self.statement_cache_capacity = capacity;
272        self
273    }
274
275    /// Sets a timeout value to wait when the database is locked, before
276    /// returning a busy timeout error.
277    ///
278    /// The default busy timeout is 5 seconds.
279    pub fn busy_timeout(mut self, timeout: Duration) -> Self {
280        self.busy_timeout = timeout;
281        self
282    }
283
284    /// Sets the [synchronous](https://www.sqlite.org/pragma.html#pragma_synchronous) setting for the database connection.
285    ///
286    /// The default synchronous settings is FULL. However, if durability is not a concern,
287    /// then NORMAL is normally all one needs in WAL mode.
288    pub fn synchronous(self, synchronous: SqliteSynchronous) -> Self {
289        self.pragma("synchronous", synchronous.as_str())
290    }
291
292    /// Sets the [auto_vacuum](https://www.sqlite.org/pragma.html#pragma_auto_vacuum) setting for the database connection.
293    ///
294    /// The default auto_vacuum setting is NONE.
295    ///
296    /// For existing databases, a change to this value does not take effect unless a
297    /// [`VACUUM` command](https://www.sqlite.org/lang_vacuum.html) is executed.
298    pub fn auto_vacuum(self, auto_vacuum: SqliteAutoVacuum) -> Self {
299        self.pragma("auto_vacuum", auto_vacuum.as_str())
300    }
301
302    /// Sets the [page_size](https://www.sqlite.org/pragma.html#pragma_page_size) setting for the database connection.
303    ///
304    /// The default page_size setting is 4096.
305    ///
306    /// For existing databases, a change to this value does not take effect unless a
307    /// [`VACUUM` command](https://www.sqlite.org/lang_vacuum.html) is executed.
308    /// However, it cannot be changed in WAL mode.
309    pub fn page_size(self, page_size: u32) -> Self {
310        self.pragma("page_size", page_size.to_string())
311    }
312
313    /// Sets custom initial pragma for the database connection.
314    pub fn pragma<K, V>(mut self, key: K, value: V) -> Self
315    where
316        K: Into<Cow<'static, str>>,
317        V: Into<Cow<'static, str>>,
318    {
319        self.pragmas.insert(key.into(), Some(value.into()));
320        self
321    }
322
323    /// Add a custom collation for comparing strings in SQL.
324    ///
325    /// If a collation with the same name already exists, it will be replaced.
326    ///
327    /// See [`sqlite3_create_collation()`](https://www.sqlite.org/c3ref/create_collation.html) for details.
328    ///
329    /// Note this excerpt:
330    /// > The collating function must obey the following properties for all strings A, B, and C:
331    /// >
332    /// > If A==B then B==A.
333    /// > If A==B and B==C then A==C.
334    /// > If A\<B then B>A.
335    /// > If A<B and B<C then A<C.
336    /// >
337    /// > If a collating function fails any of the above constraints and that collating function is
338    /// > registered and used, then the behavior of SQLite is undefined.
339    pub fn collation<N, F>(mut self, name: N, collate: F) -> Self
340    where
341        N: Into<Arc<str>>,
342        F: Fn(&str, &str) -> Ordering + Send + Sync + 'static,
343    {
344        self.collations.push(Collation::new(name, collate));
345        self
346    }
347
348    /// Add a custom function for use in SQL statements.
349    /// If a function with the same name already exists, it will be replaced.
350    /// See [`sqlite3_create_function_v2()`](https://www.sqlite.org/c3ref/create_function.html) for details.
351    ///
352    /// ### Example
353    ///
354    /// #### Unicode handling
355    ///
356    /// By default, SQLite does not handle unicode in functions like `lower` or `upper`.
357    /// To prevent binary bloat, it advises application developers to implement their own
358    /// unicode-aware functions.
359    ///
360    /// This is how you would implement a unicode-aware `lower` function:
361    ///
362    /// ```rust
363    /// # use sqlx_core_oldapi::error::Error;
364    /// use std::str::FromStr;
365    /// use sqlx::sqlite::{SqliteConnectOptions, SqliteConnection, SqliteFunctionCtx, Function};
366    /// # fn options() -> Result<SqliteConnectOptions, Error> {
367    /// let options = SqliteConnectOptions::from_str("sqlite://data.db")?
368    ///    .function(Function::new("lower", |ctx: &SqliteFunctionCtx| {
369    ///        let s = ctx.get_arg::<String>(0);
370    ///        let result = s.to_lowercase();
371    ///        ctx.set_result(result);
372    ///     }).deterministic());
373    /// # Ok(options)
374    /// # }
375    ///
376    pub fn function(mut self, func: Function) -> Self {
377        self.functions.push(func);
378        self
379    }
380
381    /// Set to `true` to signal to SQLite that the database file is on read-only media.
382    ///
383    /// If enabled, SQLite assumes the database file _cannot_ be modified, even by higher
384    /// privileged processes, and so disables locking and change detection. This is intended
385    /// to improve performance but can produce incorrect query results or errors if the file
386    /// _does_ change.
387    ///
388    /// Note that this is different from the `SQLITE_OPEN_READONLY` flag set by
389    /// [`.read_only()`][Self::read_only], though the documentation suggests that this
390    /// does _imply_ `SQLITE_OPEN_READONLY`.
391    ///
392    /// See [`sqlite3_open`](https://www.sqlite.org/capi3ref.html#sqlite3_open) (subheading
393    /// "URI Filenames") for details.
394    pub fn immutable(mut self, immutable: bool) -> Self {
395        self.immutable = immutable;
396        self
397    }
398
399    /// Sets the [threading mode](https://www.sqlite.org/threadsafe.html) for the database connection.
400    ///
401    /// The default setting is `false` corresponding to using `OPEN_NOMUTEX`.
402    /// If set to `true` then `OPEN_FULLMUTEX`.
403    ///
404    /// See [open](https://www.sqlite.org/c3ref/open.html) for more details.
405    ///
406    /// ### Note
407    /// Setting this to `true` may help if you are getting access violation errors or segmentation
408    /// faults, but will also incur a significant performance penalty. You should leave this
409    /// set to `false` if at all possible.
410    ///
411    /// If you do end up needing to set this to `true` for some reason, please
412    /// [open an issue](https://github.com/launchbadge/sqlx/issues/new/choose) as this may indicate
413    /// a concurrency bug in SQLx. Please provide clear instructions for reproducing the issue,
414    /// including a sample database schema if applicable.
415    pub fn serialized(mut self, serialized: bool) -> Self {
416        self.serialized = serialized;
417        self
418    }
419
420    /// Provide a callback to generate the name of the background worker thread.
421    ///
422    /// The value passed to the callback is an auto-incremented integer for use as the thread ID.
423    pub fn thread_name(
424        mut self,
425        generator: impl Fn(u64) -> String + Send + Sync + 'static,
426    ) -> Self {
427        self.thread_name = Arc::new(DebugFn(generator));
428        self
429    }
430
431    /// Set the maximum number of commands to buffer for the worker thread before backpressure is
432    /// applied.
433    ///
434    /// Given that most commands sent to the worker thread involve waiting for a result,
435    /// the command channel is unlikely to fill up unless a lot queries are executed in a short
436    /// period but cancelled before their full resultsets are returned.
437    pub fn command_buffer_size(mut self, size: usize) -> Self {
438        self.command_channel_size = size;
439        self
440    }
441
442    /// Set the maximum number of rows to buffer back to the calling task when a query is executed.
443    ///
444    /// If the calling task cannot keep up, backpressure will be applied to the worker thread
445    /// in order to limit CPU and memory usage.
446    pub fn row_buffer_size(mut self, size: usize) -> Self {
447        self.row_channel_size = size;
448        self
449    }
450
451    /// Sets the [`vfs`](https://www.sqlite.org/vfs.html) parameter of the database connection.
452    ///
453    /// The default value is empty, and sqlite will use the default VFS object depending on the
454    /// operating system.
455    pub fn vfs(mut self, vfs_name: impl Into<Cow<'static, str>>) -> Self {
456        self.vfs = Some(vfs_name.into());
457        self
458    }
459
460    /// Load an [extension](https://www.sqlite.org/loadext.html) at run-time when the database connection
461    /// is established, using the default entry point.
462    ///
463    /// Most common SQLite extensions can be loaded using this method, for extensions where you need
464    /// to specify the entry point, use [`extension_with_entrypoint`][`Self::extension_with_entrypoint`] instead.
465    ///
466    /// Multiple extensions can be loaded by calling the method repeatedly on the options struct, they
467    /// will be loaded in the order they are added.
468    /// ```rust,no_run
469    /// # use sqlx_core_oldapi::error::Error;
470    /// use std::str::FromStr;
471    /// use sqlx::sqlite::SqliteConnectOptions;
472    /// # fn options() -> Result<SqliteConnectOptions, Error> {
473    /// let options = SqliteConnectOptions::from_str("sqlite://data.db")?
474    ///     .extension("vsv")
475    ///     .extension("mod_spatialite");
476    /// # Ok(options)
477    /// # }
478    /// ```
479    pub fn extension(mut self, extension_name: impl Into<Cow<'static, str>>) -> Self {
480        self.extensions.insert(extension_name.into(), None);
481        self
482    }
483
484    /// Load an extension with a specified entry point.
485    ///
486    /// Useful when using non-standard extensions, or when developing your own, the second argument
487    /// specifies where SQLite should expect to find the extension init routine.
488    pub fn extension_with_entrypoint(
489        mut self,
490        extension_name: impl Into<Cow<'static, str>>,
491        entry_point: impl Into<Cow<'static, str>>,
492    ) -> Self {
493        self.extensions
494            .insert(extension_name.into(), Some(entry_point.into()));
495        self
496    }
497}