remote_settings/
schema.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
6use rusqlite::{Connection, Transaction};
7use sql_support::open_database::{self, ConnectionInitializer};
8
9/// The current gatabase schema version.
10///
11/// For any changes to the schema [`SQL`], please make sure to:
12///
13///  1. Bump this version.
14///  2. Add a migration from the old version to the new version in
15///     [`RemoteSettingsConnectionInitializer::upgrade_from`].
16pub const VERSION: u32 = 2;
17
18/// The current remote settings database schema.
19pub const SQL: &str = r#"
20CREATE TABLE IF NOT EXISTS records (
21    id TEXT PRIMARY KEY,
22    collection_url TEXT NOT NULL,
23    data BLOB NOT NULL);
24CREATE TABLE IF NOT EXISTS attachments (
25    id TEXT PRIMARY KEY,
26    collection_url TEXT NOT NULL,
27    data BLOB NOT NULL);
28CREATE TABLE IF NOT EXISTS collection_metadata (
29    collection_url TEXT PRIMARY KEY,
30    last_modified INTEGER, bucket TEXT, signature TEXT, x5u TEXT);
31"#;
32
33/// Initializes an SQLite connection to the Remote Settings database, performing
34/// migrations as needed.
35#[derive(Default)]
36pub struct RemoteSettingsConnectionInitializer;
37
38impl ConnectionInitializer for RemoteSettingsConnectionInitializer {
39    const NAME: &'static str = "remote_settings";
40    const END_VERSION: u32 = 2;
41
42    fn prepare(&self, conn: &Connection, _db_empty: bool) -> open_database::Result<()> {
43        let initial_pragmas = "
44            -- Use in-memory storage for TEMP tables.
45            PRAGMA temp_store = 2;
46            PRAGMA journal_mode = WAL;
47        ";
48        conn.execute_batch(initial_pragmas)?;
49        sql_support::debug_tools::define_debug_functions(conn)?;
50
51        Ok(())
52    }
53
54    fn init(&self, db: &Transaction<'_>) -> open_database::Result<()> {
55        db.execute_batch(SQL)?;
56        Ok(())
57    }
58
59    fn upgrade_from(&self, tx: &Transaction<'_>, version: u32) -> open_database::Result<()> {
60        match version {
61            // Upgrade from a database created before this crate used sql-support.
62            0 => {
63                tx.execute("ALTER TABLE collection_metadata DROP column fetched", ())?;
64                Ok(())
65            }
66            1 => {
67                tx.execute("ALTER TABLE collection_metadata ADD COLUMN bucket TEXT", ())?;
68                tx.execute(
69                    "ALTER TABLE collection_metadata ADD COLUMN signature TEXT",
70                    (),
71                )?;
72                tx.execute("ALTER TABLE collection_metadata ADD COLUMN x5u TEXT", ())?;
73                Ok(())
74            }
75            _ => Err(open_database::Error::IncompatibleVersion(version)),
76        }
77    }
78}
79
80#[cfg(test)]
81mod test {
82    use super::*;
83    use sql_support::open_database::test_utils::MigratedDatabaseFile;
84
85    // Snapshot of the v0 schema.  We use this to test that we can migrate from there to the
86    // current schema.
87    const V0_SCHEMA: &str = r#"
88CREATE TABLE IF NOT EXISTS records (
89    id TEXT PRIMARY KEY,
90    collection_url TEXT NOT NULL,
91    data BLOB NOT NULL);
92CREATE TABLE IF NOT EXISTS attachments (
93    id TEXT PRIMARY KEY,
94    collection_url TEXT NOT NULL,
95    data BLOB NOT NULL);
96CREATE TABLE IF NOT EXISTS collection_metadata (
97    collection_url TEXT PRIMARY KEY,
98    last_modified INTEGER,
99    fetched BOOLEAN);
100PRAGMA user_version=0;
101"#;
102
103    /// Test running all schema upgrades from V16, which was the first schema with a "real"
104    /// migration.
105    ///
106    /// If an upgrade fails, then this test will fail with a panic.
107    #[test]
108    fn test_all_upgrades() {
109        let db_file = MigratedDatabaseFile::new(RemoteSettingsConnectionInitializer, V0_SCHEMA);
110        db_file.run_all_upgrades();
111        db_file.assert_schema_matches_new_database();
112    }
113}