From 6ceed3cae9a13764262913b4d9a1b8302b213551 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Sat, 30 May 2020 15:35:51 +0300 Subject: [PATCH] sqlite3: move module to melib --- Cargo.lock | 2 +- Cargo.toml | 3 +- melib/Cargo.toml | 4 +- melib/src/lib.rs | 3 + melib/src/sqlite3.rs | 68 +++++++++++++++++++++++ src/sqlite3.rs | 129 +++++++++++++++++++------------------------ 6 files changed, 132 insertions(+), 77 deletions(-) create mode 100644 melib/src/sqlite3.rs diff --git a/Cargo.lock b/Cargo.lock index 270337396..765e9af23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -757,7 +757,6 @@ dependencies = [ "rmp", "rmp-serde", "rmpv", - "rusqlite", "serde", "serde_derive", "serde_json", @@ -790,6 +789,7 @@ dependencies = [ "notify", "notify-rust", "reqwest", + "rusqlite", "serde", "serde_derive", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 43792771a..c8e0a6c5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,7 +44,6 @@ bincode = "1.2.0" uuid = { version = "0.8.1", features = ["serde", "v4"] } unicode-segmentation = "1.2.1" # >:c libc = {version = "0.2.59", features = ["extra_traits",]} -rusqlite = {version = "0.20.0", optional =true } rmp = "^0.8" rmpv = { version = "^0.4.2", features=["with-serde",] } rmp-serde = "^0.14.0" @@ -64,7 +63,7 @@ members = ["melib", "testing", ] default = ["sqlite3", "notmuch"] notmuch = ["melib/notmuch_backend", ] jmap = ["melib/jmap_backend",] -sqlite3 = ["rusqlite"] +sqlite3 = ["melib/sqlite3"] cli-docs = [] # Print tracing logs as meli runs in stderr diff --git a/melib/Cargo.toml b/melib/Cargo.toml index 64d77ca4a..feccd5a1e 100644 --- a/melib/Cargo.toml +++ b/melib/Cargo.toml @@ -40,11 +40,12 @@ reqwest = { version ="0.10.0-alpha.2", optional=true, features = ["json", "block serde_json = { version = "1.0", optional = true, features = ["raw_value",] } smallvec = { version = "1.1.0", features = ["serde", ] } nix = "0.17.0" +rusqlite = {version = "0.20.0", optional =true } libloading = "0.5.2" [features] -default = ["unicode_algorithms", "imap_backend", "maildir_backend", "mbox_backend", "vcard"] +default = ["unicode_algorithms", "imap_backend", "maildir_backend", "mbox_backend", "vcard", "sqlite3"] debug-tracing = [] unicode_algorithms = ["unicode-segmentation"] @@ -54,3 +55,4 @@ mbox_backend = ["notify", "notify-rust", "memmap"] notmuch_backend = [] jmap_backend = ["reqwest", "serde_json" ] vcard = [] +sqlite3 = ["rusqlite", ] diff --git a/melib/src/lib.rs b/melib/src/lib.rs index 2da14a5b2..3ffc3ac79 100644 --- a/melib/src/lib.rs +++ b/melib/src/lib.rs @@ -116,6 +116,9 @@ pub use crate::thread::*; pub mod parsec; pub mod search; +#[cfg(feature = "sqlite3")] +pub mod sqlite3; + #[macro_use] extern crate serde_derive; /* parser */ diff --git a/melib/src/sqlite3.rs b/melib/src/sqlite3.rs new file mode 100644 index 000000000..20475be23 --- /dev/null +++ b/melib/src/sqlite3.rs @@ -0,0 +1,68 @@ +/* + * meli - melib + * + * Copyright 2020 Manos Pitsidianakis + * + * This file is part of meli. + * + * meli is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * meli is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with meli. If not, see . + */ + +use crate::{error::*, logging::log}; +pub use rusqlite::{self, params, Connection}; +use std::path::PathBuf; + +pub fn db_path(name: &str) -> Result { + let data_dir = + xdg::BaseDirectories::with_prefix("meli").map_err(|e| MeliError::new(e.to_string()))?; + Ok(data_dir + .place_data_file(name) + .map_err(|e| MeliError::new(e.to_string()))?) +} + +pub fn open_db(db_path: PathBuf) -> Result { + if !db_path.exists() { + return Err(MeliError::new("Database doesn't exist")); + } + Connection::open(&db_path).map_err(|e| MeliError::new(e.to_string())) +} + +pub fn open_or_create_db(name: &str, init_script: Option<&str>) -> Result { + let db_path = db_path(name)?; + let mut set_mode = false; + if !db_path.exists() { + log( + format!("Creating {} database in {}", name, db_path.display()), + crate::INFO, + ); + set_mode = true; + } + let conn = Connection::open(&db_path).map_err(|e| MeliError::new(e.to_string()))?; + if set_mode { + use std::os::unix::fs::PermissionsExt; + let file = std::fs::File::open(&db_path)?; + let metadata = file.metadata()?; + let mut permissions = metadata.permissions(); + + permissions.set_mode(0o600); // Read/write for owner only. + file.set_permissions(permissions)?; + } + + if let Some(s) = init_script { + conn.execute_batch(s) + .map_err(|e| MeliError::new(e.to_string()))?; + } + + Ok(conn) +} diff --git a/src/sqlite3.rs b/src/sqlite3.rs index 6eec88844..69ac154f4 100644 --- a/src/sqlite3.rs +++ b/src/sqlite3.rs @@ -31,78 +31,17 @@ use melib::{ email::{Envelope, EnvelopeHash}, log, thread::{SortField, SortOrder}, + sqlite3::{self as melib_sqlite3, rusqlite::{self, params}}, MeliError, Result, ERROR, }; -use rusqlite::{params, Connection}; + use smallvec::SmallVec; use std::convert::TryInto; use std::path::PathBuf; use std::sync::{Arc, RwLock}; -pub fn db_path() -> Result { - let data_dir = - xdg::BaseDirectories::with_prefix("meli").map_err(|e| MeliError::new(e.to_string()))?; - Ok(data_dir - .place_data_file("index.db") - .map_err(|e| MeliError::new(e.to_string()))?) -} - -//#[inline(always)] -//fn fts5_bareword(w: &str) -> Cow { -// if w == "AND" || w == "OR" || w == "NOT" { -// Cow::from(w) -// } else { -// if !w.is_ascii() { -// Cow::from(format!("\"{}\"", escape_double_quote(w))) -// } else { -// for &b in w.as_bytes() { -// if !(b > 0x2f && b < 0x3a) -// || !(b > 0x40 && b < 0x5b) -// || !(b > 0x60 && b < 0x7b) -// || b != 0x60 -// || b != 26 -// { -// return Cow::from(format!("\"{}\"", escape_double_quote(w))); -// } -// } -// Cow::from(w) -// } -// } -//} -// -pub fn open_db() -> Result { - let db_path = db_path()?; - if !db_path.exists() { - return Err(MeliError::new( - "Database hasn't been initialised. Run `reindex` command", - )); - } - Connection::open(&db_path).map_err(|e| MeliError::new(e.to_string())) -} - -pub fn open_or_create_db() -> Result { - let db_path = db_path()?; - let mut set_mode = false; - if !db_path.exists() { - log( - format!("Creating index database in {}", db_path.display()), - melib::INFO, - ); - set_mode = true; - } - let conn = Connection::open(&db_path).map_err(|e| MeliError::new(e.to_string()))?; - if set_mode { - use std::os::unix::fs::PermissionsExt; - let file = std::fs::File::open(&db_path)?; - let metadata = file.metadata()?; - let mut permissions = metadata.permissions(); - - permissions.set_mode(0o600); // Read/write for owner only. - file.set_permissions(permissions)?; - } - - conn.execute_batch( - "CREATE TABLE IF NOT EXISTS envelopes ( +const DB_NAME: &'static str = "index.db"; +const INIT_SCRIPT: &'static str = "CREATE TABLE IF NOT EXISTS envelopes ( id INTEGER PRIMARY KEY, account_id INTEGER REFERENCES accounts ON UPDATE CASCADE, hash BLOB NOT NULL UNIQUE, @@ -164,19 +103,49 @@ END; CREATE TRIGGER IF NOT EXISTS envelopes_au AFTER UPDATE ON envelopes BEGIN INSERT INTO fts(fts, rowid, subject, body_text) VALUES('delete', old.id, old.subject, old.body_text); INSERT INTO fts(rowid, subject, body_text) VALUES (new.id, new.subject, new.body_text); -END; ", - ) - .map_err(|e| MeliError::new(e.to_string()))?; +END; "; - Ok(conn) +pub fn db_path() -> Result { + melib_sqlite3::db_path(DB_NAME) } +//#[inline(always)] +//fn fts5_bareword(w: &str) -> Cow { +// if w == "AND" || w == "OR" || w == "NOT" { +// Cow::from(w) +// } else { +// if !w.is_ascii() { +// Cow::from(format!("\"{}\"", escape_double_quote(w))) +// } else { +// for &b in w.as_bytes() { +// if !(b > 0x2f && b < 0x3a) +// || !(b > 0x40 && b < 0x5b) +// || !(b > 0x60 && b < 0x7b) +// || b != 0x60 +// || b != 26 +// { +// return Cow::from(format!("\"{}\"", escape_double_quote(w))); +// } +// } +// Cow::from(w) +// } +// } +//} +// +// pub fn insert( envelope: &Envelope, backend: &Arc>>, acc_name: &str, ) -> Result<()> { - let conn = open_db()?; + let db_path = melib_sqlite3::db_path(DB_NAME)?; + if !db_path.exists() { + return Err(MeliError::new( + "Database hasn't been initialised. Run `reindex` command", + )); + } + + let conn = melib_sqlite3::open_db(db_path)?; let backend_lck = backend.read().unwrap(); let op = backend_lck.operation(envelope.hash()); let body = match envelope.body(op) { @@ -257,7 +226,14 @@ pub fn insert( } pub fn remove(env_hash: EnvelopeHash) -> Result<()> { - let conn = open_db()?; + let db_path = melib_sqlite3::db_path(DB_NAME)?; + if !db_path.exists() { + return Err(MeliError::new( + "Database hasn't been initialised. Run `reindex` command", + )); + } + + let conn = melib_sqlite3::open_db(db_path)?; if let Err(err) = conn .execute( "DELETE FROM envelopes WHERE hash = ?", @@ -310,7 +286,7 @@ pub fn index(context: &mut crate::state::Context, account_name: &str) -> Result< account.backend.clone(), ) }; - let conn = open_or_create_db()?; + let conn = melib_sqlite3::open_or_create_db(DB_NAME, Some(INIT_SCRIPT))?; let work_context = context.work_controller().get_context(); let env_hashes = acc_mutex .read() @@ -421,7 +397,14 @@ pub fn search( term: &str, (sort_field, sort_order): (SortField, SortOrder), ) -> Result> { - let conn = open_db()?; + let db_path = melib_sqlite3::db_path(DB_NAME)?; + if !db_path.exists() { + return Err(MeliError::new( + "Database hasn't been initialised. Run `reindex` command", + )); + } + + let conn = melib_sqlite3::open_db(db_path)?; let sort_field = match debug!(sort_field) { SortField::Subject => "subject",