sql_support/
lib.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5#![allow(unknown_lints)]
6#![warn(rust_2018_idioms)]
7
8//! A crate with various sql/sqlcipher helpers.
9
10mod conn_ext;
11pub mod debug_tools;
12
13mod each_chunk;
14mod lazy;
15pub mod maintenance;
16mod maybe_cached;
17pub mod open_database;
18pub mod path;
19mod repeat;
20
21pub use conn_ext::*;
22pub use each_chunk::*;
23pub use lazy::*;
24pub use maintenance::run_maintenance;
25pub use maybe_cached::*;
26pub use repeat::*;
27
28// reexport logging helpers.
29use error_support::{debug, info, warn};
30
31/// In PRAGMA foo='bar', `'bar'` must be a constant string (it cannot be a
32/// bound parameter), so we need to escape manually. According to
33/// <https://www.sqlite.org/faq.html>, the only character that must be escaped is
34/// the single quote, which is escaped by placing two single quotes in a row.
35pub fn escape_string_for_pragma(s: &str) -> String {
36    s.replace('\'', "''")
37}
38
39/// Default SQLite pragmas
40///
41/// Most components should just stick to these defaults.
42pub fn setup_sqlite_defaults(conn: &rusqlite::Connection) -> rusqlite::Result<()> {
43    conn.execute_batch(
44        "
45        PRAGMA temp_store = 2;
46        PRAGMA journal_mode = WAL;
47        ",
48    )?;
49    let page_size: usize = conn.query_row("PRAGMA page_size", (), |row| row.get(0))?;
50    // Aim to checkpoint at 512Kb
51    let target_checkpoint_size = 2usize.pow(19);
52    // Truncate the journal if it more than 3x larger than the target size
53    let journal_size_limit = target_checkpoint_size * 3;
54    conn.execute_batch(&format!(
55        "
56        PRAGMA wal_autocheckpoint = {};
57        PRAGMA journal_size_limit = {};
58        ",
59        target_checkpoint_size / page_size,
60        journal_size_limit,
61    ))?;
62
63    Ok(())
64}
65
66#[cfg(test)]
67mod test {
68    use super::*;
69    #[test]
70    fn test_escape_string_for_pragma() {
71        assert_eq!(escape_string_for_pragma("foobar"), "foobar");
72        assert_eq!(escape_string_for_pragma("'foo'bar'"), "''foo''bar''");
73        assert_eq!(escape_string_for_pragma("''"), "''''");
74    }
75
76    #[test]
77    fn test_sqlite_defaults() {
78        let conn = rusqlite::Connection::open_in_memory().unwrap();
79        // Simulate a default page size,
80        // On Mobile, these are set by the OS.  On Desktop, these are set by the build system when
81        // we compile SQLite.
82        conn.execute("PRAGMA page_size = 8192", ()).unwrap();
83        setup_sqlite_defaults(&conn).unwrap();
84        let autocheckpoint: usize = conn
85            .query_row("PRAGMA wal_autocheckpoint", (), |row| row.get(0))
86            .unwrap();
87        // We should aim to auto-checkpoint at 512kb, which is 64 pages when the page size is 8k
88        assert_eq!(autocheckpoint, 64);
89        // We could also check the journal size limit, but that's harder to query with a pragma.
90        // If we go the math right once, we should get it for the other case.
91    }
92}