Skip to content

Commit

Permalink
Remove r2d2_sqlite dependency
Browse files Browse the repository at this point in the history
  • Loading branch information
link2xt committed Feb 17, 2023
1 parent a34aeb2 commit 870527d
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 52 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 0 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand Down
48 changes: 8 additions & 40 deletions src/sql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -47,16 +47,19 @@ pub(crate) fn params_iter(iter: &[impl crate::ToSql]) -> impl Iterator<Item = &d
iter.iter().map(|item| item as &dyn crate::ToSql)
}

mod connection_manager;
mod migrations;

use connection_manager::ConnectionManager;

/// A wrapper around the underlying Sqlite3 object.
#[derive(Debug)]
pub struct Sql {
/// Database file path
pub(crate) dbfile: PathBuf,

/// SQL connection pool.
pool: RwLock<Option<r2d2::Pool<r2d2_sqlite::SqliteConnectionManager>>>,
pool: RwLock<Option<r2d2::Pool<ConnectionManager>>>,

/// None if the database is not open, true if it is open with passphrase and false if it is
/// open without a passphrase.
Expand Down Expand Up @@ -192,44 +195,11 @@ impl Sql {
})
}

fn new_pool(
dbfile: &Path,
passphrase: String,
) -> Result<r2d2::Pool<r2d2_sqlite::SqliteConnectionManager>> {
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<r2d2::Pool<ConnectionManager>> {
// 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)
Expand Down Expand Up @@ -393,9 +363,7 @@ impl Sql {
}

/// Allocates a connection from the connection pool and returns it.
pub async fn get_conn(
&self,
) -> Result<r2d2::PooledConnection<r2d2_sqlite::SqliteConnectionManager>> {
pub(crate) async fn get_conn(&self) -> Result<r2d2::PooledConnection<ConnectionManager>> {
let lock = self.pool.read().await;
let pool = lock.as_ref().context("no SQL connection")?;
let conn = pool.get()?;
Expand Down
73 changes: 73 additions & 0 deletions src/sql/connection_manager.rs
Original file line number Diff line number Diff line change
@@ -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<Connection, Error> {
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
}
}

0 comments on commit 870527d

Please sign in to comment.