summaryrefslogtreecommitdiffstats
path: root/third_party/rust/tabs/src/schema.rs
blob: e5ff0c7f6e12f837798b8d3a1e3a76a2396c680d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Tabs is a bit special - it's a trivial SQL schema and is only used as a persistent
// cache, and the semantics of the "tabs" collection means there's no need for
// syncChangeCounter/syncStatus nor a mirror etc.

use rusqlite::{Connection, Transaction};
use sql_support::open_database::{
    ConnectionInitializer as MigrationLogic, Error as MigrationError, Result as MigrationResult,
};

// The payload is json and this module doesn't need to deserialize, so we just
// store each "payload" as a row.
// On each Sync we delete all local rows re-populate them with every record on
// the server. When we read the DB, we also read every single record.
// So we have no primary keys, no foreign keys, and really completely waste the
// fact we are using sql.
const CREATE_SCHEMA_SQL: &str = "
    CREATE TABLE IF NOT EXISTS tabs (
        payload TEXT NOT NULL
    );
";

pub struct TabsMigrationLogin;

impl MigrationLogic for TabsMigrationLogin {
    const NAME: &'static str = "tabs storage db";
    const END_VERSION: u32 = 1;

    fn prepare(&self, conn: &Connection, _db_empty: bool) -> MigrationResult<()> {
        let initial_pragmas = "
            -- We don't care about temp tables being persisted to disk.
            PRAGMA temp_store = 2;
            -- we unconditionally want write-ahead-logging mode.
            PRAGMA journal_mode=WAL;
            -- foreign keys seem worth enforcing (and again, we don't care in practice)
            PRAGMA foreign_keys = ON;
        ";
        conn.execute_batch(initial_pragmas)?;
        // This is where we'd define our sql functions if we had any!
        conn.set_prepared_statement_cache_capacity(128);
        Ok(())
    }

    fn init(&self, db: &Transaction<'_>) -> MigrationResult<()> {
        log::debug!("Creating schema");
        db.execute_batch(CREATE_SCHEMA_SQL)?;
        Ok(())
    }

    fn upgrade_from(&self, _db: &Transaction<'_>, version: u32) -> MigrationResult<()> {
        Err(MigrationError::IncompatibleVersion(version))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::storage::TabsStorage;

    #[test]
    fn test_create_schema_twice() {
        let mut db = TabsStorage::new_with_mem_path("test");
        let conn = db.open_or_create().unwrap();
        conn.execute_batch(CREATE_SCHEMA_SQL)
            .expect("should allow running twice");
    }
}