imap: update cache on set_flags

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
pull/315/head
Manos Pitsidianakis 2023-11-27 09:23:50 +02:00
parent 0500e451da
commit 23507932f9
Signed by: Manos Pitsidianakis
GPG Key ID: 7729C7707F7E09D0
6 changed files with 98 additions and 10 deletions

View File

@ -38,7 +38,7 @@ impl Account {
let handle = self
.main_loop_handler
.job_executor
.spawn_specialized("set_unseen".into(), fut);
.spawn_specialized("set_flags".into(), fut);
let job_id = handle.job_id;
self.insert_job(
job_id,

View File

@ -42,7 +42,7 @@ nom = { version = "7" }
notify = { version = "4.0.15", optional = true }
polling = "2.8"
regex = { version = "1" }
rusqlite = { version = "^0.29", default-features = false, optional = true }
rusqlite = { version = "^0.29", default-features = false, features = ["array"], optional = true }
serde = { version = "1.0", features = ["rc"] }
serde_derive = "1.0"
serde_json = { version = "1.0", features = ["raw_value"] }

View File

@ -92,6 +92,13 @@ pub trait ImapCache: Send + std::fmt::Debug {
identifier: std::result::Result<UID, EnvelopeHash>,
mailbox_hash: MailboxHash,
) -> Result<Option<Vec<u8>>>;
fn update_flags(
&mut self,
env_hashes: EnvelopeHashBatch,
mailbox_hash: MailboxHash,
flags: SmallVec<[FlagOp; 8]>,
) -> Result<()>;
}
pub trait ImapCacheReset: Send + std::fmt::Debug {
@ -108,7 +115,7 @@ pub mod sqlite3_m {
use super::*;
use crate::utils::sqlite3::{
self,
rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput},
rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, Value},
Connection, DatabaseDescription,
};
@ -151,6 +158,12 @@ pub mod sqlite3_m {
version: 3,
};
impl From<EnvelopeHash> for Value {
fn from(env_hash: EnvelopeHash) -> Self {
(env_hash.0 as i64).into()
}
}
impl ToSql for ModSequence {
fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> {
Ok(ToSqlOutput::from(self.0.get() as i64))
@ -539,6 +552,67 @@ pub mod sqlite3_m {
Ok(())
}
fn update_flags(
&mut self,
env_hashes: EnvelopeHashBatch,
mailbox_hash: MailboxHash,
flags: SmallVec<[FlagOp; 8]>,
) -> Result<()> {
if self.mailbox_state(mailbox_hash)?.is_none() {
return Err(Error::new("Mailbox is not in cache").set_kind(ErrorKind::Bug));
}
let Self {
ref mut connection,
ref uid_store,
loaded_mailboxes: _,
} = self;
let tx = connection.transaction()?;
let values =
std::rc::Rc::new(env_hashes.iter().map(Value::from).collect::<Vec<Value>>());
let mut stmt =
tx.prepare("SELECT uid, envelope FROM envelopes WHERE hash IN rarray(?1);")?;
let rows = stmt
.query_map([values], |row| Ok((row.get(0)?, row.get(1)?)))?
.filter_map(|r| r.ok())
.collect::<Vec<(UID, Envelope)>>();
drop(stmt);
let mut stmt = tx.prepare(
"UPDATE envelopes SET envelope = ?1 WHERE mailbox_hash = ?2 AND uid = ?3;",
)?;
for (uid, mut env) in rows {
for op in flags.iter() {
match op {
FlagOp::UnSet(flag) | FlagOp::Set(flag) => {
let mut f = env.flags();
f.set(*flag, op.as_bool());
env.set_flags(f);
}
FlagOp::UnSetTag(tag) | FlagOp::SetTag(tag) => {
let hash = TagHash::from_bytes(tag.as_bytes());
if op.as_bool() {
env.tags_mut().insert(hash);
} else {
env.tags_mut().remove(&hash);
}
}
}
}
stmt.execute(sqlite3::params![&env, mailbox_hash, uid as Sqlite3UID])?;
uid_store
.envelopes
.lock()
.unwrap()
.entry(env.hash())
.and_modify(|entry| {
entry.inner = env;
});
}
drop(stmt);
tx.commit()?;
Ok(())
}
fn update(
&mut self,
mailbox_hash: MailboxHash,
@ -829,5 +903,14 @@ pub mod default_m {
) -> Result<Option<Vec<u8>>> {
Err(Error::new("melib is not built with any imap cache").set_kind(ErrorKind::Bug))
}
fn update_flags(
&mut self,
_env_hashes: EnvelopeHashBatch,
_mailbox_hash: MailboxHash,
_flags: SmallVec<[FlagOp; 8]>,
) -> Result<()> {
Err(Error::new("melib is not built with any imap cache").set_kind(ErrorKind::Bug))
}
}
}

View File

@ -857,6 +857,12 @@ impl MailBackend for ImapType {
}
}
}
#[cfg(feature = "sqlite3")]
if *uid_store.keep_offline_cache.lock().unwrap() {
let mut cache_handle = cache::Sqlite3Cache::get(uid_store.clone())?;
let res = cache_handle.update_flags(env_hashes, mailbox_hash, flags);
log::trace!("update_flags in cache: {:?}", res);
}
Ok(())
}))
}

View File

@ -102,11 +102,6 @@ pub async fn idle(kit: ImapWatchKit) -> Result<()> {
mailbox_hash,
kind: RefreshEventKind::Rescan,
});
/*
uid_store.uid_index.lock().unwrap().clear();
uid_store.hash_index.lock().unwrap().clear();
uid_store.byte_cache.lock().unwrap().clear();
*/
}
} else {
uidvalidities.insert(mailbox_hash, select_response.uidvalidity);

View File

@ -45,7 +45,10 @@ pub fn open_db(db_path: PathBuf) -> Result<Connection> {
if !db_path.exists() {
return Err(Error::new("Database doesn't exist"));
}
Connection::open(&db_path).map_err(|e| Error::new(e.to_string()))
Ok(Connection::open(&db_path).and_then(|db| {
rusqlite::vtab::array::load_module(&db)?;
Ok(db)
})?)
}
pub fn open_or_create_db(
@ -66,7 +69,8 @@ pub fn open_or_create_db(
db_path.display()
);
}
let conn = Connection::open(&db_path).map_err(|e| Error::new(e.to_string()))?;
let conn = Connection::open(&db_path)?;
rusqlite::vtab::array::load_module(&conn)?;
if set_mode {
use std::os::unix::fs::PermissionsExt;
let file = std::fs::File::open(&db_path)?;