mirror of
https://github.com/chatmail/core.git
synced 2026-04-19 14:36:29 +03:00
Merge branch 'master' into flub-param-names
This commit is contained in:
127
src/sql.rs
127
src/sql.rs
@@ -1,7 +1,8 @@
|
||||
use std::collections::HashSet;
|
||||
use std::sync::RwLock;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use rusqlite::{Connection, OpenFlags, Statement, NO_PARAMS};
|
||||
use thread_local_object::ThreadLocal;
|
||||
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
@@ -16,12 +17,14 @@ const DC_OPEN_READONLY: usize = 0x01;
|
||||
/// A wrapper around the underlying Sqlite3 object.
|
||||
pub struct Sql {
|
||||
pool: RwLock<Option<r2d2::Pool<r2d2_sqlite::SqliteConnectionManager>>>,
|
||||
in_use: Arc<ThreadLocal<String>>,
|
||||
}
|
||||
|
||||
impl Sql {
|
||||
pub fn new() -> Sql {
|
||||
Sql {
|
||||
pool: RwLock::new(None),
|
||||
in_use: Arc::new(ThreadLocal::new()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +34,7 @@ impl Sql {
|
||||
|
||||
pub fn close(&self, context: &Context) {
|
||||
let _ = self.pool.write().unwrap().take();
|
||||
self.in_use.remove();
|
||||
// drop closes the connection
|
||||
|
||||
info!(context, 0, "Database closed.");
|
||||
@@ -53,6 +57,7 @@ impl Sql {
|
||||
P: IntoIterator,
|
||||
P::Item: rusqlite::ToSql,
|
||||
{
|
||||
self.start_stmt(sql.to_string());
|
||||
self.with_conn(|conn| conn.execute(sql, params).map_err(Into::into))
|
||||
}
|
||||
|
||||
@@ -60,22 +65,25 @@ impl Sql {
|
||||
where
|
||||
G: FnOnce(&Connection) -> Result<T>,
|
||||
{
|
||||
match &*self.pool.read().unwrap() {
|
||||
let res = match &*self.pool.read().unwrap() {
|
||||
Some(pool) => {
|
||||
let conn = pool.get()?;
|
||||
g(&conn)
|
||||
}
|
||||
None => Err(Error::SqlNoConnection),
|
||||
}
|
||||
};
|
||||
self.in_use.remove();
|
||||
res
|
||||
}
|
||||
|
||||
pub fn prepare<G, H>(&self, sql: &str, g: G) -> Result<H>
|
||||
where
|
||||
G: FnOnce(Statement<'_>) -> Result<H>,
|
||||
G: FnOnce(Statement<'_>, &Connection) -> Result<H>,
|
||||
{
|
||||
self.start_stmt(sql.to_string());
|
||||
self.with_conn(|conn| {
|
||||
let stmt = conn.prepare(sql)?;
|
||||
let res = g(stmt)?;
|
||||
let res = g(stmt, conn)?;
|
||||
Ok(res)
|
||||
})
|
||||
}
|
||||
@@ -84,6 +92,7 @@ impl Sql {
|
||||
where
|
||||
G: FnOnce(Statement<'_>, Statement<'_>, &Connection) -> Result<H>,
|
||||
{
|
||||
self.start_stmt(format!("{} - {}", sql1, sql2));
|
||||
self.with_conn(|conn| {
|
||||
let stmt1 = conn.prepare(sql1)?;
|
||||
let stmt2 = conn.prepare(sql2)?;
|
||||
@@ -109,8 +118,8 @@ impl Sql {
|
||||
F: FnMut(&rusqlite::Row) -> rusqlite::Result<T>,
|
||||
G: FnMut(rusqlite::MappedRows<F>) -> Result<H>,
|
||||
{
|
||||
self.start_stmt(sql.as_ref().to_string());
|
||||
self.with_conn(|conn| {
|
||||
eprintln!("query_map {}", sql.as_ref());
|
||||
let mut stmt = conn.prepare(sql.as_ref())?;
|
||||
let res = stmt.query_map(params, f)?;
|
||||
g(res)
|
||||
@@ -124,6 +133,7 @@ impl Sql {
|
||||
P: IntoIterator,
|
||||
P::Item: rusqlite::ToSql,
|
||||
{
|
||||
self.start_stmt(sql.to_string());
|
||||
self.with_conn(|conn| {
|
||||
let mut stmt = conn.prepare(sql)?;
|
||||
let res = stmt.exists(params)?;
|
||||
@@ -138,11 +148,12 @@ impl Sql {
|
||||
P::Item: rusqlite::ToSql,
|
||||
F: FnOnce(&rusqlite::Row) -> rusqlite::Result<T>,
|
||||
{
|
||||
self.start_stmt(sql.as_ref().to_string());
|
||||
self.with_conn(|conn| conn.query_row(sql.as_ref(), params, f).map_err(Into::into))
|
||||
}
|
||||
|
||||
pub fn table_exists(&self, name: impl AsRef<str>) -> bool {
|
||||
self.with_conn(|conn| Ok(table_exists(conn, name)))
|
||||
self.with_conn(|conn| table_exists(conn, name))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
@@ -260,18 +271,27 @@ impl Sql {
|
||||
pub fn get_config_int64(&self, context: &Context, key: impl AsRef<str>) -> Option<i64> {
|
||||
self.get_config(context, key).and_then(|r| r.parse().ok())
|
||||
}
|
||||
|
||||
fn start_stmt(&self, stmt: impl AsRef<str>) {
|
||||
if let Some(query) = self.in_use.get_cloned() {
|
||||
let bt = backtrace::Backtrace::new();
|
||||
eprintln!("old query: {}", query);
|
||||
eprintln!("Connection is already used from this thread: {:?}", bt);
|
||||
panic!("Connection is already used from this thread");
|
||||
}
|
||||
|
||||
self.in_use.set(stmt.as_ref().to_string());
|
||||
}
|
||||
}
|
||||
|
||||
fn table_exists(conn: &Connection, name: impl AsRef<str>) -> bool {
|
||||
fn table_exists(conn: &Connection, name: impl AsRef<str>) -> Result<bool> {
|
||||
let mut exists = false;
|
||||
conn.pragma(None, "table_info", &format!("{}", name.as_ref()), |_row| {
|
||||
// will only be executed if the info was found
|
||||
exists = true;
|
||||
Ok(())
|
||||
})
|
||||
.expect("bad sqlite state");
|
||||
|
||||
exists
|
||||
})?;
|
||||
Ok(exists)
|
||||
}
|
||||
|
||||
fn open(
|
||||
@@ -842,10 +862,22 @@ pub fn get_rowid(
|
||||
table: impl AsRef<str>,
|
||||
field: impl AsRef<str>,
|
||||
value: impl AsRef<str>,
|
||||
) -> u32 {
|
||||
sql.start_stmt("get rowid".to_string());
|
||||
sql.with_conn(|conn| Ok(get_rowid_with_conn(context, conn, table, field, value)))
|
||||
.unwrap_or_else(|_| 0)
|
||||
}
|
||||
|
||||
pub fn get_rowid_with_conn(
|
||||
context: &Context,
|
||||
conn: &Connection,
|
||||
table: impl AsRef<str>,
|
||||
field: impl AsRef<str>,
|
||||
value: impl AsRef<str>,
|
||||
) -> u32 {
|
||||
// alternative to sqlite3_last_insert_rowid() which MUST NOT be used due to race conditions, see comment above.
|
||||
// the ORDER BY ensures, this function always returns the most recent id,
|
||||
// eg. if a Message-ID is splitted into different messages.
|
||||
// eg. if a Message-ID is split into different messages.
|
||||
let query = format!(
|
||||
"SELECT id FROM {} WHERE {}='{}' ORDER BY id DESC",
|
||||
table.as_ref(),
|
||||
@@ -853,7 +885,7 @@ pub fn get_rowid(
|
||||
value.as_ref()
|
||||
);
|
||||
|
||||
match sql.query_row(&query, NO_PARAMS, |row| row.get::<_, u32>(0)) {
|
||||
match conn.query_row(&query, NO_PARAMS, |row| row.get::<_, u32>(0)) {
|
||||
Ok(id) => id,
|
||||
Err(err) => {
|
||||
error!(
|
||||
@@ -864,7 +896,6 @@ pub fn get_rowid(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_rowid2(
|
||||
context: &Context,
|
||||
sql: &Sql,
|
||||
@@ -874,6 +905,7 @@ pub fn get_rowid2(
|
||||
field2: impl AsRef<str>,
|
||||
value2: i32,
|
||||
) -> u32 {
|
||||
sql.start_stmt("get rowid2".to_string());
|
||||
sql.with_conn(|conn| {
|
||||
Ok(get_rowid2_with_conn(
|
||||
context, conn, table, field, value, field2, value2,
|
||||
@@ -973,31 +1005,36 @@ pub fn housekeeping(context: &Context) {
|
||||
}
|
||||
let entry = entry.unwrap();
|
||||
let name_f = entry.file_name();
|
||||
let name_c = to_cstring(name_f.to_string_lossy());
|
||||
let name_c = unsafe { to_cstring(name_f.to_string_lossy()) };
|
||||
|
||||
if unsafe {
|
||||
is_file_in_use(&mut files_in_use, 0 as *const libc::c_char, name_c.as_ptr())
|
||||
} || unsafe {
|
||||
is_file_in_use(
|
||||
&mut files_in_use,
|
||||
b".increation\x00" as *const u8 as *const libc::c_char,
|
||||
name_c.as_ptr(),
|
||||
)
|
||||
} || unsafe {
|
||||
is_file_in_use(
|
||||
&mut files_in_use,
|
||||
b".waveform\x00" as *const u8 as *const libc::c_char,
|
||||
name_c.as_ptr(),
|
||||
)
|
||||
} || unsafe {
|
||||
is_file_in_use(
|
||||
&mut files_in_use,
|
||||
b"-preview.jpg\x00" as *const u8 as *const libc::c_char,
|
||||
name_c.as_ptr(),
|
||||
)
|
||||
} {
|
||||
if unsafe { is_file_in_use(&mut files_in_use, 0 as *const libc::c_char, name_c) }
|
||||
|| unsafe {
|
||||
is_file_in_use(
|
||||
&mut files_in_use,
|
||||
b".increation\x00" as *const u8 as *const libc::c_char,
|
||||
name_c,
|
||||
)
|
||||
}
|
||||
|| unsafe {
|
||||
is_file_in_use(
|
||||
&mut files_in_use,
|
||||
b".waveform\x00" as *const u8 as *const libc::c_char,
|
||||
name_c,
|
||||
)
|
||||
}
|
||||
|| unsafe {
|
||||
is_file_in_use(
|
||||
&mut files_in_use,
|
||||
b"-preview.jpg\x00" as *const u8 as *const libc::c_char,
|
||||
name_c,
|
||||
)
|
||||
}
|
||||
{
|
||||
unsafe { free(name_c as *mut _) };
|
||||
continue;
|
||||
}
|
||||
unsafe { free(name_c as *mut _) };
|
||||
|
||||
unreferenced_count += 1;
|
||||
|
||||
match std::fs::metadata(entry.path()) {
|
||||
@@ -1029,8 +1066,11 @@ pub fn housekeeping(context: &Context) {
|
||||
unreferenced_count,
|
||||
entry.file_name()
|
||||
);
|
||||
let path = to_cstring(entry.path().to_str().unwrap());
|
||||
unsafe { dc_delete_file(context, path.as_ptr()) };
|
||||
unsafe {
|
||||
let path = to_cstring(entry.path().to_str().unwrap());
|
||||
dc_delete_file(context, path);
|
||||
free(path as *mut _);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
@@ -1088,14 +1128,16 @@ fn maybe_add_from_param(
|
||||
context
|
||||
.sql
|
||||
.query_row(query, NO_PARAMS, |row| {
|
||||
let v = to_cstring(row.get::<_, String>(0)?);
|
||||
unsafe {
|
||||
dc_param_set_packed(param, v.as_ptr() as *const libc::c_char);
|
||||
let file = dc_param_get(param, param_id, 0 as *const libc::c_char);
|
||||
let v = to_cstring(row.get::<_, String>(0)?);
|
||||
dc_param_set_packed(param, v as *const _);
|
||||
let file = dc_param_get(param, param_id, 0 as *const _);
|
||||
if !file.is_null() {
|
||||
maybe_add_file(files_in_use, as_str(file));
|
||||
free(file as *mut libc::c_void);
|
||||
}
|
||||
|
||||
free(v as *mut _);
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
@@ -1129,7 +1171,6 @@ mod test {
|
||||
maybe_add_file(&mut files, "$BLOBDIR/world.txt");
|
||||
maybe_add_file(&mut files, "world2.txt");
|
||||
|
||||
println!("{:?}", files);
|
||||
assert!(unsafe {
|
||||
is_file_in_use(
|
||||
&mut files,
|
||||
|
||||
Reference in New Issue
Block a user