diff --git a/CHANGELOG.md b/CHANGELOG.md index f3781b5d3e..be08622eea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - deltachat-rpc-client: use `dataclass` for `Account`, `Chat`, `Contact` and `Message` #4042 - python: mark bindings as supporting typing according to PEP 561 #4045 - retry filesystem operations during account migration #4043 +- remove `r2d2_sqlite` dependency #4050 ### Fixes - deltachat-rpc-server: do not block stdin while processing the request. #4041 diff --git a/Cargo.lock b/Cargo.lock index 097a270597..90f72d4f5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -935,7 +935,6 @@ dependencies = [ "qrcodegen", "quick-xml", "r2d2", - "r2d2_sqlite", "rand 0.8.5", "ratelimit", "regex", @@ -2816,16 +2815,6 @@ dependencies = [ "scheduled-thread-pool", ] -[[package]] -name = "r2d2_sqlite" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fdc8e4da70586127893be32b7adf21326a4c6b1aba907611edf467d13ffe895" -dependencies = [ - "r2d2", - "rusqlite", -] - [[package]] name = "radix_trie" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index e74796f5fd..459ee7c385 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,6 @@ pgp = { version = "0.9", default-features = false } pretty_env_logger = { version = "0.4", optional = true } quick-xml = "0.27" r2d2 = "0.8" -r2d2_sqlite = "0.20" rand = "0.8" regex = "1.7" rusqlite = { version = "0.27", features = ["sqlcipher"] } diff --git a/src/sql.rs b/src/sql.rs index f103ace393..c6315f6808 100644 --- a/src/sql.rs +++ b/src/sql.rs @@ -7,7 +7,7 @@ use std::path::PathBuf; use std::time::Duration; use anyhow::{bail, Context as _, Result}; -use rusqlite::{config::DbConfig, Connection, OpenFlags}; +use rusqlite::{self, config::DbConfig, Connection}; use tokio::sync::RwLock; use crate::blob::BlobObject; @@ -47,8 +47,11 @@ pub(crate) fn params_iter(iter: &[impl crate::ToSql]) -> impl Iterator>>, + pool: RwLock>>, /// None if the database is not open, true if it is open with passphrase and false if it is /// open without a passphrase. @@ -192,44 +195,11 @@ impl Sql { }) } - fn new_pool( - dbfile: &Path, - passphrase: String, - ) -> Result> { - let mut open_flags = OpenFlags::SQLITE_OPEN_NO_MUTEX; - open_flags.insert(OpenFlags::SQLITE_OPEN_READ_WRITE); - open_flags.insert(OpenFlags::SQLITE_OPEN_CREATE); - + fn new_pool(dbfile: &Path, passphrase: String) -> Result> { // this actually creates min_idle database handles just now. // therefore, with_init() must not try to modify the database as otherwise // we easily get busy-errors (eg. table-creation, journal_mode etc. should be done on only one handle) - let mgr = r2d2_sqlite::SqliteConnectionManager::file(dbfile) - .with_flags(open_flags) - .with_init(move |c| { - c.execute_batch(&format!( - "PRAGMA cipher_memory_security = OFF; -- Too slow on Android - PRAGMA secure_delete=on; - PRAGMA busy_timeout = {}; - PRAGMA temp_store=memory; -- Avoid SQLITE_IOERR_GETTEMPPATH errors on Android - PRAGMA foreign_keys=on; - ", - Duration::from_secs(60).as_millis() - ))?; - c.pragma_update(None, "key", passphrase.clone())?; - // Try to enable auto_vacuum. This will only be - // applied if the database is new or after successful - // VACUUM, which usually happens before backup export. - // When auto_vacuum is INCREMENTAL, it is possible to - // use PRAGMA incremental_vacuum to return unused - // database pages to the filesystem. - c.pragma_update(None, "auto_vacuum", "INCREMENTAL".to_string())?; - - c.pragma_update(None, "journal_mode", "WAL".to_string())?; - // Default synchronous=FULL is much slower. NORMAL is sufficient for WAL mode. - c.pragma_update(None, "synchronous", "NORMAL".to_string())?; - Ok(()) - }); - + let mgr = ConnectionManager::new(dbfile.to_path_buf(), passphrase); let pool = r2d2::Pool::builder() .min_idle(Some(2)) .max_size(10) @@ -393,9 +363,7 @@ impl Sql { } /// Allocates a connection from the connection pool and returns it. - pub async fn get_conn( - &self, - ) -> Result> { + pub(crate) async fn get_conn(&self) -> Result> { let lock = self.pool.read().await; let pool = lock.as_ref().context("no SQL connection")?; let conn = pool.get()?; diff --git a/src/sql/connection_manager.rs b/src/sql/connection_manager.rs new file mode 100644 index 0000000000..f0d556c799 --- /dev/null +++ b/src/sql/connection_manager.rs @@ -0,0 +1,73 @@ +use std::path::PathBuf; +use std::time::Duration; + +use r2d2::ManageConnection; +use rusqlite::{Connection, Error, OpenFlags}; + +#[derive(Debug)] +pub struct ConnectionManager { + /// Database file path. + path: PathBuf, + + /// SQLite open flags. + flags: rusqlite::OpenFlags, + + /// SQLCipher database passphrase. + /// Empty string if database is not encrypted. + passphrase: String, +} + +impl ConnectionManager { + /// Creates new connection manager. + pub fn new(path: PathBuf, passphrase: String) -> Self { + let mut flags = OpenFlags::SQLITE_OPEN_NO_MUTEX; + flags.insert(OpenFlags::SQLITE_OPEN_READ_WRITE); + flags.insert(OpenFlags::SQLITE_OPEN_CREATE); + + Self { + path, + flags, + passphrase, + } + } +} + +impl ManageConnection for ConnectionManager { + type Connection = Connection; + type Error = Error; + + fn connect(&self) -> Result { + let conn = Connection::open_with_flags(&self.path, self.flags)?; + conn.execute_batch(&format!( + "PRAGMA cipher_memory_security = OFF; -- Too slow on Android + PRAGMA secure_delete=on; + PRAGMA busy_timeout = {}; + PRAGMA temp_store=memory; -- Avoid SQLITE_IOERR_GETTEMPPATH errors on Android + PRAGMA foreign_keys=on; + ", + Duration::from_secs(60).as_millis() + ))?; + conn.pragma_update(None, "key", &self.passphrase)?; + // Try to enable auto_vacuum. This will only be + // applied if the database is new or after successful + // VACUUM, which usually happens before backup export. + // When auto_vacuum is INCREMENTAL, it is possible to + // use PRAGMA incremental_vacuum to return unused + // database pages to the filesystem. + conn.pragma_update(None, "auto_vacuum", "INCREMENTAL".to_string())?; + + conn.pragma_update(None, "journal_mode", "WAL".to_string())?; + // Default synchronous=FULL is much slower. NORMAL is sufficient for WAL mode. + conn.pragma_update(None, "synchronous", "NORMAL".to_string())?; + + Ok(conn) + } + + fn is_valid(&self, _conn: &mut Connection) -> Result<(), Error> { + Ok(()) + } + + fn has_broken(&self, _conn: &mut Connection) -> bool { + false + } +}