sqlite3: add accounts and folders table

master
Manos Pitsidianakis 2019-11-11 18:01:01 +02:00
parent 1d6ef92a4f
commit 9cd00cf53a
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
2 changed files with 86 additions and 29 deletions

View File

@ -223,7 +223,7 @@ impl Account {
pub fn new( pub fn new(
index: usize, index: usize,
name: String, name: String,
settings: AccountConf, mut settings: AccountConf,
map: &Backends, map: &Backends,
work_context: WorkContext, work_context: WorkContext,
notify_fn: NotifyFn, notify_fn: NotifyFn,
@ -253,6 +253,10 @@ impl Account {
} else { } else {
AddressBook::new(name.clone()) AddressBook::new(name.clone())
}; };
if settings.account().format() == "imap" {
settings.conf.cache_type = crate::conf::CacheType::None;
}
let mut ret = Account { let mut ret = Account {
index, index,
name, name,
@ -503,7 +507,9 @@ impl Account {
}; };
#[cfg(feature = "sqlite3")] #[cfg(feature = "sqlite3")]
{ {
if let Err(err) = crate::sqlite3::insert(&envelope, &self.backend) { if let Err(err) =
crate::sqlite3::insert(&envelope, &self.backend, &self.name)
{
melib::log( melib::log(
format!( format!(
"Failed to insert envelope {} in cache: {}", "Failed to insert envelope {} in cache: {}",

View File

@ -82,6 +82,7 @@ pub fn open_db() -> Result<Connection> {
conn.execute_batch( conn.execute_batch(
"CREATE TABLE IF NOT EXISTS envelopes ( "CREATE TABLE IF NOT EXISTS envelopes (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
account_id INTEGER REFERENCES accounts ON UPDATE CASCADE,
hash BLOB NOT NULL, hash BLOB NOT NULL,
date TEXT NOT NULL, date TEXT NOT NULL,
_from TEXT NOT NULL, _from TEXT NOT NULL,
@ -96,24 +97,28 @@ pub fn open_db() -> Result<Connection> {
has_attachments BOOLEAN NOT NULL, has_attachments BOOLEAN NOT NULL,
body_text TEXT NOT NULL, body_text TEXT NOT NULL,
timestamp BLOB NOT NULL timestamp BLOB NOT NULL
); );
CREATE TABLE IF NOT EXISTS folders ( CREATE TABLE IF NOT EXISTS folders (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
account_id INTEGER NOT NULL REFERENCES accounts ON UPDATE CASCADE,
hash BLOB NOT NULL, hash BLOB NOT NULL,
date TEXT NOT NULL, date TEXT NOT NULL,
_from TEXT NOT NULL, name TEXT NOT NULL
_to TEXT NOT NULL,
cc TEXT NOT NULL,
bcc TEXT NOT NULL,
subject TEXT NOT NULL,
message_id TEXT NOT NULL,
in_reply_to TEXT NOT NULL,
_references TEXT NOT NULL,
flags INTEGER NOT NULL,
has_attachments BOOLEAN NOT NULL,
body_text TEXT NOT NULL,
timestamp BLOB NOT NULL
); );
CREATE TABLE IF NOT EXISTS accounts (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS folder_and_envelope (
folder_id INTEGER NOT NULL,
envelope_id INTEGER NOT NULL,
PRIMARY KEY (folder_id, envelope_id),
FOREIGN KEY(folder_id) REFERENCES folders(id) ON UPDATE CASCADE,
FOREIGN KEY(envelope_id) REFERENCES envelopes(id) ON UPDATE CASCADE
);
CREATE INDEX IF NOT EXISTS folder_env_idx ON folder_and_envelope(folder_id);
CREATE INDEX IF NOT EXISTS env_folder_idx ON folder_and_envelope(envelope_id);
CREATE UNIQUE INDEX IF NOT EXISTS acc_idx ON accounts(name);
CREATE INDEX IF NOT EXISTS envelope_timestamp_index ON envelopes (timestamp); CREATE INDEX IF NOT EXISTS envelope_timestamp_index ON envelopes (timestamp);
@ -144,7 +149,7 @@ END; ",
Ok(conn) Ok(conn)
} }
pub fn insert(envelope: &Envelope, backend: &Arc<RwLock<Box<dyn MailBackend>>>) -> Result<()> { pub fn insert(envelope: &Envelope, backend: &Arc<RwLock<Box<dyn MailBackend>>>, acc_name: &str) -> Result<()> {
let conn = open_db()?; let conn = open_db()?;
let backend_lck = backend.read().unwrap(); let backend_lck = backend.read().unwrap();
let op = backend_lck.operation(envelope.hash()); let op = backend_lck.operation(envelope.hash());
@ -170,10 +175,15 @@ pub fn insert(envelope: &Envelope, backend: &Arc<RwLock<Box<dyn MailBackend>>>)
return Err(err); return Err(err);
} }
}; };
let account_id: i32 = {
let mut stmt = conn.prepare("SELECT id FROM accounts WHERE name = ?").unwrap();
let x = stmt.query_map(params![acc_name], |row| row.get(0)).unwrap().next().unwrap().unwrap();
x
};
if let Err(err) = conn.execute( if let Err(err) = conn.execute(
"INSERT OR REPLACE INTO envelopes (hash, date, _from, _to, cc, bcc, subject, message_id, in_reply_to, _references, flags, has_attachments, body_text, timestamp) "INSERT OR REPLACE INTO envelopes (account_id, hash, date, _from, _to, cc, bcc, subject, message_id, in_reply_to, _references, flags, has_attachments, body_text, timestamp)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14)", VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15)",
params![envelope.hash().to_be_bytes().to_vec(), envelope.date_as_str(), envelope.field_from_to_string(), envelope.field_to_to_string(), envelope.field_cc_to_string(), envelope.field_bcc_to_string(), envelope.subject().into_owned().trim_end_matches('\u{0}'), envelope.message_id_display().to_string(), envelope.in_reply_to_display().map(|f| f.to_string()).unwrap_or(String::new()), envelope.field_references_to_string(), i64::from(envelope.flags().bits()), if envelope.has_attachments() { 1 } else { 0 }, body, envelope.date().to_be_bytes().to_vec()], params![acc_name, envelope.hash().to_be_bytes().to_vec(), envelope.date_as_str(), envelope.field_from_to_string(), envelope.field_to_to_string(), envelope.field_cc_to_string(), envelope.field_bcc_to_string(), envelope.subject().into_owned().trim_end_matches('\u{0}'), envelope.message_id_display().to_string(), envelope.in_reply_to_display().map(|f| f.to_string()).unwrap_or(String::new()), envelope.field_references_to_string(), i64::from(envelope.flags().bits()), if envelope.has_attachments() { 1 } else { 0 }, body, envelope.date().to_be_bytes().to_vec()],
) )
.map_err(|e| MeliError::new(e.to_string())) { .map_err(|e| MeliError::new(e.to_string())) {
debug!( debug!(
@ -199,11 +209,17 @@ pub fn index(context: &mut crate::state::Context) -> Result<()> {
.accounts .accounts
.iter() .iter()
.filter(|acc| *acc.settings.conf.cache_type() == crate::conf::CacheType::Sqlite3) .filter(|acc| *acc.settings.conf.cache_type() == crate::conf::CacheType::Sqlite3)
.map(|acc| (acc.collection.envelopes.clone(), acc.backend.clone())) .map(|acc| {
.collect::<Vec<(Arc<RwLock<_>>, Arc<_>)>>(); (
acc.name().to_string(),
acc.collection.envelopes.clone(),
acc.backend.clone(),
)
})
.collect::<Vec<(String, Arc<RwLock<_>>, Arc<_>)>>();
let env_hashes = mutexes let env_hashes = mutexes
.iter() .iter()
.map(|m| m.0.read().unwrap().keys().cloned().collect::<Vec<_>>()) .map(|m| m.1.read().unwrap().keys().cloned().collect::<Vec<_>>())
.collect::<Vec<Vec<_>>>(); .collect::<Vec<Vec<_>>>();
/* Sleep, index and repeat in order not to block the main process */ /* Sleep, index and repeat in order not to block the main process */
@ -211,12 +227,32 @@ pub fn index(context: &mut crate::state::Context) -> Result<()> {
let thread_id = std::thread::current().id(); let thread_id = std::thread::current().id();
let sleep_dur = std::time::Duration::from_millis(20); let sleep_dur = std::time::Duration::from_millis(20);
for ((acc_mutex, backend_mutex), env_hashes) in mutexes.into_iter().zip(env_hashes.into_iter()) { for ((acc_name, acc_mutex, backend_mutex), env_hashes) in mutexes.into_iter().zip(env_hashes.into_iter()) {
if let Err(err) = conn.execute(
"INSERT OR REPLACE INTO accounts (name) VALUES (?1)", params![acc_name.as_str(),],).map_err(|e| MeliError::new(e.to_string())) {
debug!("{}",
format!(
"Failed to update index: {}",
err.to_string()
));
log(
format!(
"Failed to update index: {}",
err.to_string()
),
ERROR,
);
}
let account_id: i32 = {
let mut stmt = conn.prepare("SELECT id FROM accounts WHERE name = ?").unwrap();
let x = stmt.query_map(params![acc_name.as_str()], |row| row.get(0)).unwrap().next().unwrap().unwrap();
x
};
let mut ctr = 0; let mut ctr = 0;
debug!("{}", format!("Rebuilding index. {}/{}", ctr, env_hashes.len())); debug!("{}", format!("Rebuilding {} index. {}/{}", acc_name, ctr, env_hashes.len()));
work_context work_context
.set_status .set_status
.send((thread_id, format!("Rebuilding index. {}/{}", ctr, env_hashes.len()))) .send((thread_id, format!("Rebuilding {} index. {}/{}", acc_name, ctr, env_hashes.len())))
.unwrap(); .unwrap();
for chunk in env_hashes.chunks(200) { for chunk in env_hashes.chunks(200) {
ctr += chunk.len(); ctr += chunk.len();
@ -246,9 +282,9 @@ pub fn index(context: &mut crate::state::Context) -> Result<()> {
} }
}; };
if let Err(err) = conn.execute( if let Err(err) = conn.execute(
"INSERT OR REPLACE INTO envelopes (hash, date, _from, _to, cc, bcc, subject, message_id, in_reply_to, _references, flags, has_attachments, body_text, timestamp) "INSERT OR REPLACE INTO envelopes (account_id, hash, date, _from, _to, cc, bcc, subject, message_id, in_reply_to, _references, flags, has_attachments, body_text, timestamp)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14)", VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15)",
params![e.hash().to_be_bytes().to_vec(), e.date_as_str(), e.field_from_to_string(), e.field_to_to_string(), e.field_cc_to_string(), e.field_bcc_to_string(), e.subject().into_owned().trim_end_matches('\u{0}'), e.message_id_display().to_string(), e.in_reply_to_display().map(|f| f.to_string()).unwrap_or(String::new()), e.field_references_to_string(), i64::from(e.flags().bits()), if e.has_attachments() { 1 } else { 0 }, body, e.date().to_be_bytes().to_vec()], params![account_id, e.hash().to_be_bytes().to_vec(), e.date_as_str(), e.field_from_to_string(), e.field_to_to_string(), e.field_cc_to_string(), e.field_bcc_to_string(), e.subject().into_owned().trim_end_matches('\u{0}'), e.message_id_display().to_string(), e.in_reply_to_display().map(|f| f.to_string()).unwrap_or(String::new()), e.field_references_to_string(), i64::from(e.flags().bits()), if e.has_attachments() { 1 } else { 0 }, body, e.date().to_be_bytes().to_vec()],
) )
.map_err(|e| MeliError::new(e.to_string())) { .map_err(|e| MeliError::new(e.to_string())) {
debug!("{}", debug!("{}",
@ -271,7 +307,7 @@ pub fn index(context: &mut crate::state::Context) -> Result<()> {
drop(envelopes_lck); drop(envelopes_lck);
work_context work_context
.set_status .set_status
.send((thread_id, format!("Rebuilding index. {}/{}", ctr, env_hashes.len()))) .send((thread_id, format!("Rebuilding {} index. {}/{}", acc_name, ctr, env_hashes.len())))
.unwrap(); .unwrap();
std::thread::sleep(sleep_dur); std::thread::sleep(sleep_dur);
} }
@ -343,6 +379,21 @@ pub fn query_to_sql(q: &Query) -> String {
s.extend(escape_double_quote(t).chars()); s.extend(escape_double_quote(t).chars());
s.push_str("%\" "); s.push_str("%\" ");
} }
To(t) => {
s.push_str("_to LIKE \"%");
s.extend(escape_double_quote(t).chars());
s.push_str("%\" ");
}
Cc(t) => {
s.push_str("cc LIKE \"%");
s.extend(escape_double_quote(t).chars());
s.push_str("%\" ");
}
Bcc(t) => {
s.push_str("bcc LIKE \"%");
s.extend(escape_double_quote(t).chars());
s.push_str("%\" ");
}
AllText(t) => { AllText(t) => {
s.push_str("body_text LIKE \"%"); s.push_str("body_text LIKE \"%");
s.extend(escape_double_quote(t).chars()); s.extend(escape_double_quote(t).chars());