Cache and Sqlite3 cleanups

sql
Manos Pitsidianakis 2019-11-07 22:32:42 +02:00
parent e396b2f72b
commit 27edd96493
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
2 changed files with 68 additions and 176 deletions

View File

@ -19,13 +19,18 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
/*
use melib::backends::{FolderHash, MailBackend};
use melib::mailbox::*;
use melib::thread::{ThreadHash, ThreadNode};
use std::sync::RwLock;
*/
use melib::email::{Flag, UnixTimestamp};
use melib::parsec::*;
use melib::{
backends::{FolderHash, MailBackend},
email::EnvelopeHash,
thread::{SortField, SortOrder},
Result, StackVec,
};
use std::sync::{Arc, RwLock};
pub use query_parser::query;
use Query::*;
#[derive(Debug, PartialEq)]
pub enum Query {
@ -52,103 +57,11 @@ pub enum Query {
Not(Box<Query>),
}
/*
enum CacheType {
Sqlite3,
}
pub struct Cache {
collection: Collection,
kind: CacheType,
backend: Box<dyn MailBackend>,
}
impl Cache {
pub fn build_index(&mut self) {
unimplemented!()
}
pub fn new(backend: Box<dyn MailBackend>) -> Self {
unimplemented!()
}
pub fn get_env(&self, h: &EnvelopeHash) -> &Envelope {
&self.collection[h]
}
pub fn get_env_mut(&mut self, h: &EnvelopeHash) -> &mut Envelope {
self.collection.entry(*h).or_default()
}
pub fn contains_key(&self, h: EnvelopeHash) -> bool {
self.collection.contains_key(&h)
}
/*
pub fn operation(&self, h: EnvelopeHash) -> Box<dyn BackendOp> {
//let operation = self.backend.operation(h, m.folder.hash())
unimplemented!()
unreachable!()
}
*/
pub fn thread_to_mail_mut(&mut self, h: ThreadHash, f: FolderHash) -> &mut Envelope {
self.collection
.envelopes
.entry(self.collection.threads[&f].thread_to_mail(h))
.or_default()
}
pub fn thread_to_mail(&self, h: ThreadHash, f: FolderHash) -> &Envelope {
&self.collection.envelopes[&self.collection.threads[&f].thread_to_mail(h)]
}
pub fn threaded_mail(&self, h: ThreadHash, f: FolderHash) -> EnvelopeHash {
self.collection.threads[&f].thread_to_mail(h)
}
pub fn mail_and_thread(
&mut self,
i: EnvelopeHash,
f: FolderHash,
) -> (&mut Envelope, &ThreadNode) {
let thread;
{
let x = &mut self.collection.envelopes.entry(i).or_default();
thread = &self.collection.threads[&f][&x.thread()];
}
(self.collection.envelopes.entry(i).or_default(), thread)
}
pub fn thread(&self, h: ThreadHash, f: FolderHash) -> &ThreadNode {
&self.collection.threads[&f].thread_nodes()[&h]
}
}
*/
impl std::ops::Not for Query {
type Output = Query;
fn not(self) -> Query {
match self {
Query::Not(q) => *q,
q => Query::Not(Box::new(q)),
}
}
}
impl std::ops::BitAnd for Query {
type Output = Self;
fn bitand(self, rhs: Self) -> Self {
Query::And(Box::new(self), Box::new(rhs))
}
}
impl std::ops::BitOr for Query {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
Query::Or(Box::new(self), Box::new(rhs))
}
}
pub use query_parser::query;
pub mod query_parser {
use super::Query::{self, *};
use melib::parsec::*;
pub fn subject<'a>() -> impl Parser<'a, Query> {
fn subject<'a>() -> impl Parser<'a, Query> {
prefix(
whitespace_wrap(match_literal("subject:")),
whitespace_wrap(literal()),
@ -156,7 +69,7 @@ pub mod query_parser {
.map(|term| Query::Subject(term))
}
pub fn from<'a>() -> impl Parser<'a, Query> {
fn from<'a>() -> impl Parser<'a, Query> {
prefix(
whitespace_wrap(match_literal("from:")),
whitespace_wrap(literal()),
@ -164,7 +77,7 @@ pub mod query_parser {
.map(|term| Query::From(term))
}
pub fn or<'a>() -> impl Parser<'a, Query> {
fn or<'a>() -> impl Parser<'a, Query> {
move |input| {
whitespace_wrap(match_literal_anycase("or"))
.parse(input)
@ -172,7 +85,7 @@ pub mod query_parser {
}
}
pub fn not<'a>() -> impl Parser<'a, Query> {
fn not<'a>() -> impl Parser<'a, Query> {
move |input| {
whitespace_wrap(either(
match_literal_anycase("not"),
@ -183,7 +96,7 @@ pub mod query_parser {
}
}
pub fn and<'a>() -> impl Parser<'a, Query> {
fn and<'a>() -> impl Parser<'a, Query> {
move |input| {
whitespace_wrap(match_literal_anycase("and"))
.parse(input)
@ -191,11 +104,11 @@ pub mod query_parser {
}
}
pub fn literal<'a>() -> impl Parser<'a, String> {
fn literal<'a>() -> impl Parser<'a, String> {
move |input| either(quoted_string(), string()).parse(input)
}
pub fn parentheses_query<'a>() -> impl Parser<'a, Query> {
fn parentheses_query<'a>() -> impl Parser<'a, Query> {
move |input| {
delimited(
whitespace_wrap(match_literal("(")),
@ -206,6 +119,18 @@ pub mod query_parser {
}
}
/// Parser from `String` to `Query`.
///
/// # Invocation
/// ```
/// use ui::cache::query;
/// use ui::cache::Query;
/// use melib::parsec::Parser;
///
/// let input = "test";
/// let query = query().parse(input);
/// assert_eq!(Ok(("", Query::AllText("test".to_string()))), query);
/// ```
pub fn query<'a>() -> impl Parser<'a, Query> {
move |input| {
let (rest, query_a): (&'a str, Query) = if let Ok(q) = parentheses_query().parse(input)

View File

@ -43,29 +43,29 @@ fn escape_double_quote(w: &str) -> Cow<str> {
}
}
#[inline(always)]
fn fts5_bareword(w: &str) -> Cow<str> {
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)
}
}
}
//#[inline(always)]
//fn fts5_bareword(w: &str) -> Cow<str> {
// 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<Connection> {
let data_dir =
xdg::BaseDirectories::with_prefix("meli").map_err(|e| MeliError::new(e.to_string()))?;
@ -302,11 +302,6 @@ pub fn search(
SortOrder::Desc => "DESC",
};
/*
debug!("SELECT hash FROM envelopes INNER JOIN fts ON fts.rowid = envelopes.id WHERE fts MATCH ? ORDER BY {} {};", sort_field, sort_order);
let mut stmt = conn.prepare(
format!("SELECT hash FROM envelopes INNER JOIN fts ON fts.rowid = envelopes.id WHERE fts MATCH ? ORDER BY {} {};", sort_field, sort_order).as_str())
*/
let mut stmt = conn
.prepare(
debug!(format!(
@ -334,69 +329,43 @@ pub fn search(
results
}
pub fn from(term: &str) -> Result<StackVec<EnvelopeHash>> {
let conn = open_db()?;
let mut stmt = conn
.prepare("SELECT hash FROM envelopes WHERE _from LIKE ?;")
.map_err(|e| MeliError::new(e.to_string()))?;
let results = stmt
.query_map(&[term.trim()], |row| Ok(row.get(0)?))
.map_err(|e| MeliError::new(e.to_string()))?
.map(|r: std::result::Result<Vec<u8>, rusqlite::Error>| {
Ok(u64::from_be_bytes(
r.map_err(|e| MeliError::new(e.to_string()))?
.as_slice()
.try_into()
.map_err(|e: std::array::TryFromSliceError| MeliError::new(e.to_string()))?,
))
})
.collect::<Result<StackVec<EnvelopeHash>>>();
results
}
/// Translates a `Query` to an Sqlite3 expression in a `String`.
pub fn query_to_sql(q: &Query) -> String {
fn rec(q: &Query, s: &mut String) {
match q {
Subject(t) => {
s.push_str(" subject LIKE \"%");
s.push_str("subject LIKE \"%");
s.extend(escape_double_quote(t).chars());
s.push_str("%\"");
s.push_str("%\" ");
}
From(t) => {
s.push_str(" _from LIKE \"%");
s.push_str("_from LIKE \"%");
s.extend(escape_double_quote(t).chars());
s.push_str("%\"");
s.push_str("%\" ");
}
AllText(t) => {
s.push_str(" body_text LIKE \"%");
s.push_str("body_text LIKE \"%");
s.extend(escape_double_quote(t).chars());
s.push_str("%\"");
s.push_str("%\" ");
}
And(q1, q2) => {
s.push_str(" (");
s.push_str("(");
rec(q1, s);
s.push_str(") ");
s.push_str(" AND ");
s.push_str(" (");
s.push_str(") AND (");
rec(q2, s);
s.push_str(") ");
}
Or(q1, q2) => {
s.push_str(" (");
s.push_str("(");
rec(q1, s);
s.push_str(") ");
s.push_str(" OR ");
s.push_str(" (");
s.push_str(") OR (");
rec(q2, s);
s.push_str(") ");
}
Not(q) => {
s.push_str(" NOT ");
s.push_str("(");
s.push_str("NOT (");
rec(q, s);
s.push_str(")");
s.push_str(") ");
}
_ => {}
}
@ -404,18 +373,16 @@ pub fn query_to_sql(q: &Query) -> String {
let mut ret = String::new();
rec(q, &mut ret);
ret
//"SELECT hash FROM envelopes INNER JOIN fts ON fts.rowid = envelopes.id WHERE fts MATCH ? ORDER BY {} {};"
}
#[test]
fn test_query_to_sql() {
assert_eq!(
" subject LIKE \"%test%\" AND body_text LIKE \"%i%\"",
"(subject LIKE \"%test%\" ) AND (body_text LIKE \"%i%\" ) ",
&query_to_sql(&query().parse_complete("subject: test and i").unwrap().1)
);
assert_eq!(
" subject LIKE \"%github%\" OR ( _from LIKE \"%epilys%\" AND ( subject LIKE \"%lib%\" OR subject LIKE \"%meli%\") ) ",
"(subject LIKE \"%github%\" ) OR ((_from LIKE \"%epilys%\" ) AND ((subject LIKE \"%lib%\" ) OR (subject LIKE \"%meli%\" ) ) ) ",
&query_to_sql(
&query()
.parse_complete(