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_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(&path)?
26            },
27            None => Connection::open_in_memory()?,
28        };
29        // Initialize the database with necessary pragmas
30        for pragma in DB_PRAGMAS.iter() {
31            let _ = connection.execute(pragma, []);
32        }
33        Ok(SqliteEngine { connection })
34    }
35
36    pub fn init_db(path: &PathBuf) -> rusqlite::Result<Connection> {
37        if let Some(parent) = path.parent() {
38            let _ = std::fs::create_dir_all(parent);
39        }
40        let connection = Connection::open(path)?;
41        for pragma in DB_INIT_PRAGMAS.iter() {
42            let _ = connection.execute(pragma, []);
43        }
44        connection.execute("CREATE TABLE IF NOT EXISTS data (id INTEGER PRIMARY KEY AUTOINCREMENT, key TEXT, value TEXT);", [])?;
45        Ok(connection)
46    }
47}
48
49impl WebStorageEngine for SqliteEngine {
50    type Error = rusqlite::Error;
51
52    fn load(&self) -> Result<OriginEntry, Self::Error> {
53        let mut stmt = self.connection.prepare("SELECT key, value FROM data;")?;
54        let rows = stmt.query_map([], |row| {
55            let key: String = row.get(0)?;
56            let value: String = row.get(1)?;
57            Ok((key, value))
58        })?;
59
60        let mut map = OriginEntry::default();
61        for row in rows {
62            let (key, value) = row?;
63            map.insert(key, value);
64        }
65        Ok(map)
66    }
67
68    fn clear(&mut self) -> Result<(), Self::Error> {
69        self.connection.execute("DELETE FROM data;", [])?;
70        Ok(())
71    }
72
73    fn delete(&mut self, key: &str) -> Result<(), Self::Error> {
74        self.connection
75            .execute("DELETE FROM data WHERE key = ?;", [key])?;
76        Ok(())
77    }
78
79    fn set(&mut self, key: &str, value: &str) -> Result<(), Self::Error> {
80        // update or insert
81        self.connection.execute(
82            "INSERT INTO data (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value=excluded.value;",
83            [key, value],
84        )?;
85        Ok(())
86    }
87
88    fn save(&mut self, data: &OriginEntry) {
89        fn save_inner(conn: &mut Connection, data: &OriginEntry) -> rusqlite::Result<()> {
90            let tx = conn.transaction()?;
91            tx.execute("DELETE FROM data;", [])?;
92            let mut stmt = tx.prepare("INSERT INTO data (key, value) VALUES (?, ?);")?;
93            for (key, value) in data.inner() {
94                stmt.execute(rusqlite::params![key, value])?;
95            }
96            drop(stmt);
97            tx.commit()?;
98            Ok(())
99        }
100        if let Err(e) = save_inner(&mut self.connection, data) {
101            error!("localstorage save error: {:?}", e);
102        }
103    }
104}