Merge branch 'master' into flub-param-names

This commit is contained in:
holger krekel
2019-07-22 00:24:04 +02:00
49 changed files with 1254 additions and 933 deletions

View File

@@ -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,