// Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html use std::collections::HashMap; use rusqlite::params; use serde::de::DeserializeOwned; use serde_json::Value; use super::SqliteStorage; use crate::{config::ConfigEntry, error::Result, timestamp::TimestampSecs, types::Usn}; impl SqliteStorage { pub(crate) fn set_config_entry(&self, entry: &ConfigEntry) -> Result<()> { self.db .prepare_cached(include_str!("add.sql"))? .execute(params![&entry.key, entry.usn, entry.mtime, &entry.value])?; Ok(()) } pub(crate) fn remove_config(&self, key: &str) -> Result<()> { self.db .prepare_cached("delete from config where key=?")? .execute([key])?; Ok(()) } pub(crate) fn get_config_value(&self, key: &str) -> Result> { self.db .prepare_cached(include_str!("get.sql"))? .query_and_then([key], |row| { let blob = row.get_ref_unwrap(0).as_blob()?; serde_json::from_slice(blob).map_err(Into::into) })? .next() .transpose() } /// Return the raw bytes and other metadata, for undoing. pub(crate) fn get_config_entry(&self, key: &str) -> Result>> { self.db .prepare_cached(include_str!("get_entry.sql"))? .query_and_then([key], |row| { Ok(ConfigEntry::boxed( key, row.get(0)?, row.get(1)?, row.get(2)?, )) })? .next() .transpose() } /// Prefix is expected to end with '_'. pub(crate) fn get_config_prefix(&self, prefix: &str) -> Result)>> { let mut end = prefix.to_string(); assert_eq!(end.pop(), Some('_')); end.push(std::char::from_u32('_' as u32 + 1).unwrap()); self.db .prepare("select key, val from config where key > ? and key < ?")? .query_and_then(params![prefix, &end], |row| Ok((row.get(0)?, row.get(1)?)))? .collect() } pub(crate) fn get_all_config(&self) -> Result> { self.db .prepare("select key, val from config")? .query_and_then([], |row| { let val: Value = serde_json::from_slice(row.get_ref_unwrap(1).as_blob()?)?; Ok((row.get::(0)?, val)) })? .collect() } pub(crate) fn set_all_config( &self, conf: HashMap, usn: Usn, mtime: TimestampSecs, ) -> Result<()> { self.db.execute("delete from config", [])?; for (key, val) in conf.iter() { self.set_config_entry(&ConfigEntry::boxed( key, serde_json::to_vec(&val)?, usn, mtime, ))?; } Ok(()) } pub(crate) fn clear_config_usns(&self) -> Result<()> { self.db .prepare("update config set usn = 0 where usn != 0")? .execute([])?; Ok(()) } // Upgrading/downgrading pub(super) fn upgrade_config_to_schema14(&self) -> Result<()> { let conf = self .db .query_row_and_then("select conf from col", [], |row| { let conf: Result> = serde_json::from_str(row.get_ref_unwrap(0).as_str()?).map_err(Into::into); conf })?; self.set_all_config(conf, Usn(0), TimestampSecs(0))?; self.db.execute_batch("update col set conf=''")?; Ok(()) } pub(super) fn downgrade_config_from_schema14(&self) -> Result<()> { let allconf = self.get_all_config()?; self.db .execute("update col set conf=?", [serde_json::to_string(&allconf)?])?; Ok(()) } }