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 */
56use rusqlite::{Connection, Transaction};
7use sql_support::open_database::{self, ConnectionInitializer};
89/// 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;
1718/// 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"#;
3233/// Initializes an SQLite connection to the Remote Settings database, performing
34/// migrations as needed.
35#[derive(Default)]
36pub struct RemoteSettingsConnectionInitializer;
3738impl ConnectionInitializer for RemoteSettingsConnectionInitializer {
39const NAME: &'static str = "remote_settings";
40const END_VERSION: u32 = 2;
4142fn prepare(&self, conn: &Connection, _db_empty: bool) -> open_database::Result<()> {
43let 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)?;
5051Ok(())
52 }
5354fn init(&self, db: &Transaction<'_>) -> open_database::Result<()> {
55 db.execute_batch(SQL)?;
56Ok(())
57 }
5859fn upgrade_from(&self, tx: &Transaction<'_>, version: u32) -> open_database::Result<()> {
60match version {
61// Upgrade from a database created before this crate used sql-support.
620 => {
63 tx.execute("ALTER TABLE collection_metadata DROP column fetched", ())?;
64Ok(())
65 }
661 => {
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", ())?;
73Ok(())
74 }
75_ => Err(open_database::Error::IncompatibleVersion(version)),
76 }
77 }
78}
7980#[cfg(test)]
81mod test {
82use super::*;
83use sql_support::open_database::test_utils::MigratedDatabaseFile;
8485// Snapshot of the v0 schema. We use this to test that we can migrate from there to the
86 // current schema.
87const 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"#;
102103/// 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]
108fn test_all_upgrades() {
109let db_file = MigratedDatabaseFile::new(RemoteSettingsConnectionInitializer, V0_SCHEMA);
110 db_file.run_all_upgrades();
111 db_file.assert_schema_matches_new_database();
112 }
113}