storage/webstorage/engines/
sqlite.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 https://mozilla.org/MPL/2.0/. */
4
5use std::path::PathBuf;
6use std::sync::Arc;
7
8use base::threadpool::ThreadPool;
9use log::error;
10use rusqlite::Connection;
11
12use crate::shared::{DB_IN_MEMORY_INIT_PRAGMAS, DB_IN_MEMORY_PRAGMAS, DB_INIT_PRAGMAS, DB_PRAGMAS};
13use crate::webstorage::OriginEntry;
14use crate::webstorage::engines::WebStorageEngine;
15
16pub struct SqliteEngine {
17    connection: Connection,
18}
19
20impl SqliteEngine {
21    pub fn new(db_dir: &Option<PathBuf>, _pool: Arc<ThreadPool>) -> rusqlite::Result<Self> {
22        let connection = match db_dir {
23            Some(path) => {
24                let path = path.join("webstorage.sqlite");
25                Self::init_db(Some(&path))?
26            },
27            None => Self::init_db(None)?,
28        };
29        Ok(SqliteEngine { connection })
30    }
31
32    pub fn init_db(db_path: Option<&PathBuf>) -> rusqlite::Result<Connection> {
33        let connection = if let Some(path) = db_path {
34            if let Some(parent) = path.parent() {
35                let _ = std::fs::create_dir_all(parent);
36            }
37            let conn = Connection::open(path)?;
38            for pragma in DB_INIT_PRAGMAS.iter() {
39                let _ = conn.execute(pragma, []);
40            }
41            for pragma in DB_PRAGMAS.iter() {
42                let _ = conn.execute(pragma, []);
43            }
44            conn
45        } else {
46            // TODO We probably don't need an in memory implementation at all.
47            // WebStorageEnvironment already keeps all key value pairs in memory via its data field.
48            // A future refactoring could avoid creating a WebStorageEngine entirely when config_dir is None.
49            let conn = Connection::open_in_memory()?;
50            for pragma in DB_IN_MEMORY_INIT_PRAGMAS.iter() {
51                let _ = conn.execute(pragma, []);
52            }
53            for pragma in DB_IN_MEMORY_PRAGMAS.iter() {
54                let _ = conn.execute(pragma, []);
55            }
56            conn
57        };
58        connection.execute("CREATE TABLE IF NOT EXISTS data (id INTEGER PRIMARY KEY AUTOINCREMENT, key TEXT, value TEXT);", [])?;
59        Ok(connection)
60    }
61}
62
63impl WebStorageEngine for SqliteEngine {
64    type Error = rusqlite::Error;
65
66    fn load(&self) -> Result<OriginEntry, Self::Error> {
67        let mut stmt = self.connection.prepare("SELECT key, value FROM data;")?;
68        let rows = stmt.query_map([], |row| {
69            let key: String = row.get(0)?;
70            let value: String = row.get(1)?;
71            Ok((key, value))
72        })?;
73
74        let mut map = OriginEntry::default();
75        for row in rows {
76            let (key, value) = row?;
77            map.insert(key, value);
78        }
79        Ok(map)
80    }
81
82    fn clear(&mut self) -> Result<(), Self::Error> {
83        self.connection.execute("DELETE FROM data;", [])?;
84        Ok(())
85    }
86
87    fn delete(&mut self, key: &str) -> Result<(), Self::Error> {
88        self.connection
89            .execute("DELETE FROM data WHERE key = ?;", [key])?;
90        Ok(())
91    }
92
93    fn set(&mut self, key: &str, value: &str) -> Result<(), Self::Error> {
94        // update or insert
95        //
96        // TODO: Replace this with an UPSERT once the schema guarantees a
97        // UNIQUE/PRIMARY KEY constraint on `key`.
98        let tx = self.connection.transaction()?;
99        let rows = tx.execute("UPDATE data SET value = ? WHERE key = ?", [value, key])?;
100        if rows == 0 {
101            tx.execute("INSERT INTO data (key, value) VALUES (?, ?)", [key, value])?;
102        }
103        tx.commit()?;
104        Ok(())
105    }
106
107    fn save(&mut self, data: &OriginEntry) {
108        fn save_inner(conn: &mut Connection, data: &OriginEntry) -> rusqlite::Result<()> {
109            let tx = conn.transaction()?;
110            tx.execute("DELETE FROM data;", [])?;
111            let mut stmt = tx.prepare("INSERT INTO data (key, value) VALUES (?, ?);")?;
112            for (key, value) in data.inner() {
113                stmt.execute(rusqlite::params![key, value])?;
114            }
115            drop(stmt);
116            tx.commit()?;
117            Ok(())
118        }
119        if let Err(e) = save_inner(&mut self.connection, data) {
120            error!("localstorage save error: {:?}", e);
121        }
122    }
123}