Move SQL errors into their own module

This commit is contained in:
Alexander Krotov
2019-11-29 23:06:13 +01:00
committed by Floris Bruynooghe
parent ea8adf39c2
commit 612600278a
9 changed files with 102 additions and 66 deletions

View File

@@ -64,13 +64,15 @@ impl Chat {
); );
match res { match res {
Err(err @ crate::error::Error::Sql(rusqlite::Error::QueryReturnedNoRows)) => Err(err), Err(err @ crate::sql::Error::Sql(rusqlite::Error::QueryReturnedNoRows)) => {
Err(err.into())
}
Err(err) => { Err(err) => {
error!( error!(
context, context,
"chat: failed to load from db {}: {:?}", chat_id, err "chat: failed to load from db {}: {:?}", chat_id, err
); );
Err(err) Err(err.into())
} }
Ok(mut chat) => { Ok(mut chat) => {
match chat.id { match chat.id {
@@ -129,7 +131,8 @@ impl Chat {
&context.sql, &context.sql,
"UPDATE chats SET param=? WHERE id=?", "UPDATE chats SET param=? WHERE id=?",
params![self.param.to_string(), self.id as i32], params![self.param.to_string(), self.id as i32],
) )?;
Ok(())
} }
pub fn get_id(&self) -> u32 { pub fn get_id(&self) -> u32 {
@@ -681,7 +684,7 @@ pub fn lookup_by_contact_id(context: &Context, contact_id: u32) -> Result<(u32,
"SELECT c.id, c.blocked FROM chats c INNER JOIN chats_contacts j ON c.id=j.chat_id WHERE c.type=100 AND c.id>9 AND j.contact_id=?;", "SELECT c.id, c.blocked FROM chats c INNER JOIN chats_contacts j ON c.id=j.chat_id WHERE c.type=100 AND c.id>9 AND j.contact_id=?;",
params![contact_id as i32], params![contact_id as i32],
|row| Ok((row.get(0)?, row.get::<_, Option<_>>(1)?.unwrap_or_default())), |row| Ok((row.get(0)?, row.get::<_, Option<_>>(1)?.unwrap_or_default())),
) ).map_err(Into::into)
} }
pub fn get_by_contact_id(context: &Context, contact_id: u32) -> Result<u32, Error> { pub fn get_by_contact_id(context: &Context, contact_id: u32) -> Result<u32, Error> {
@@ -829,7 +832,8 @@ pub fn unarchive(context: &Context, chat_id: u32) -> Result<(), Error> {
&context.sql, &context.sql,
"UPDATE chats SET archived=0 WHERE id=?", "UPDATE chats SET archived=0 WHERE id=?",
params![chat_id as i32], params![chat_id as i32],
) )?;
Ok(())
} }
/// Send a message defined by a dc_msg_t object to a chat. /// Send a message defined by a dc_msg_t object to a chat.
@@ -976,7 +980,8 @@ fn do_set_draft(context: &Context, chat_id: u32, msg: &mut Message) -> Result<()
msg.param.to_string(), msg.param.to_string(),
1, 1,
], ],
) )?;
Ok(())
} }
// similar to as dc_set_draft() but does not emit an event // similar to as dc_set_draft() but does not emit an event
@@ -1688,7 +1693,7 @@ pub fn is_group_explicitly_left(context: &Context, grpid: impl AsRef<str>) -> Re
context.sql.exists( context.sql.exists(
"SELECT id FROM leftgrps WHERE grpid=?;", "SELECT id FROM leftgrps WHERE grpid=?;",
params![grpid.as_ref()], params![grpid.as_ref()],
) ).map_err(Into::into)
} }
pub fn set_chat_name( pub fn set_chat_name(

View File

@@ -7,7 +7,6 @@ use crate::blob::BlobObject;
use crate::constants::DC_VERSION_STR; use crate::constants::DC_VERSION_STR;
use crate::context::Context; use crate::context::Context;
use crate::dc_tools::*; use crate::dc_tools::*;
use crate::error::Error;
use crate::job::*; use crate::job::*;
use crate::stock::StockMessage; use crate::stock::StockMessage;
@@ -113,7 +112,7 @@ impl Context {
/// Set the given config key. /// Set the given config key.
/// If `None` is passed as a value the value is cleared and set to the default if there is one. /// If `None` is passed as a value the value is cleared and set to the default if there is one.
pub fn set_config(&self, key: Config, value: Option<&str>) -> Result<(), Error> { pub fn set_config(&self, key: Config, value: Option<&str>) -> crate::sql::Result<()> {
match key { match key {
Config::Selfavatar if value.is_some() => { Config::Selfavatar if value.is_some() => {
let blob = BlobObject::create_from_path(&self, value.unwrap())?; let blob = BlobObject::create_from_path(&self, value.unwrap())?;

View File

@@ -141,7 +141,7 @@ pub enum VerifiedStatus {
} }
impl Contact { impl Contact {
pub fn load_from_db(context: &Context, contact_id: u32) -> Result<Self> { pub fn load_from_db(context: &Context, contact_id: u32) -> crate::sql::Result<Self> {
if contact_id == DC_CONTACT_ID_SELF { if contact_id == DC_CONTACT_ID_SELF {
let contact = Contact { let contact = Contact {
id: contact_id, id: contact_id,
@@ -691,7 +691,7 @@ impl Contact {
} }
Err(err) => { Err(err) => {
error!(context, "delete_contact {} failed ({})", contact_id, err); error!(context, "delete_contact {} failed ({})", contact_id, err);
return Err(err); return Err(err.into());
} }
} }
} }
@@ -709,7 +709,7 @@ impl Contact {
/// like "Me" in the selected language and the email address /// like "Me" in the selected language and the email address
/// defined by dc_set_config(). /// defined by dc_set_config().
pub fn get_by_id(context: &Context, contact_id: u32) -> Result<Contact> { pub fn get_by_id(context: &Context, contact_id: u32) -> Result<Contact> {
Contact::load_from_db(context, contact_id) Ok(Contact::load_from_db(context, contact_id)?)
} }
/// Get the ID of the contact. /// Get the ID of the contact.

View File

@@ -2,18 +2,10 @@ use failure::Fail;
#[derive(Debug, Fail)] #[derive(Debug, Fail)]
pub enum Error { pub enum Error {
#[fail(display = "Sqlite Error: {:?}", _0)]
Sql(rusqlite::Error),
#[fail(display = "Sqlite Connection Pool Error: {:?}", _0)]
ConnectionPool(r2d2::Error),
#[fail(display = "{:?}", _0)] #[fail(display = "{:?}", _0)]
Failure(failure::Error), Failure(failure::Error),
#[fail(display = "Sqlite: Connection closed")] #[fail(display = "SQL error: {:?}", _0)]
SqlNoConnection, SqlError(#[cause] crate::sql::Error),
#[fail(display = "Sqlite: Already open")]
SqlAlreadyOpen,
#[fail(display = "Sqlite: Failed to open")]
SqlFailedToOpen,
#[fail(display = "{:?}", _0)] #[fail(display = "{:?}", _0)]
Io(std::io::Error), Io(std::io::Error),
#[fail(display = "{:?}", _0)] #[fail(display = "{:?}", _0)]
@@ -40,9 +32,9 @@ pub enum Error {
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
impl From<rusqlite::Error> for Error { impl From<crate::sql::Error> for Error {
fn from(err: rusqlite::Error) -> Error { fn from(err: crate::sql::Error) -> Error {
Error::Sql(err) Error::SqlError(err)
} }
} }
@@ -58,12 +50,6 @@ impl From<failure::Error> for Error {
} }
} }
impl From<r2d2::Error> for Error {
fn from(err: r2d2::Error) -> Error {
Error::ConnectionPool(err)
}
}
impl From<std::io::Error> for Error { impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Error { fn from(err: std::io::Error) -> Error {
Error::Io(err) Error::Io(err)

View File

@@ -462,7 +462,9 @@ fn import_backup(context: &Context, backup_to_import: impl AsRef<Path>) -> Resul
|files| { |files| {
for (processed_files_cnt, file) in files.enumerate() { for (processed_files_cnt, file) in files.enumerate() {
let (file_name, file_blob) = file?; let (file_name, file_blob) = file?;
ensure!(!context.shall_stop_ongoing(), "received stop signal"); if context.shall_stop_ongoing() {
return Ok(false);
}
let mut permille = processed_files_cnt * 1000 / total_files_cnt; let mut permille = processed_files_cnt * 1000 / total_files_cnt;
if permille < 10 { if permille < 10 {
permille = 10 permille = 10
@@ -476,26 +478,25 @@ fn import_backup(context: &Context, backup_to_import: impl AsRef<Path>) -> Resul
} }
let path_filename = context.get_blobdir().join(file_name); let path_filename = context.get_blobdir().join(file_name);
if dc_write_file(context, &path_filename, &file_blob).is_err() { dc_write_file(context, &path_filename, &file_blob)?;
bail!(
"Storage full? Cannot write file {} with {} bytes.",
path_filename.display(),
file_blob.len(),
);
} else {
continue;
}
} }
Ok(()) Ok(true)
}, },
); );
res.and_then(|_| { match res {
// only delete backup_blobs if all files were successfully extracted Ok(all_files_extracted) => {
sql::execute(context, &context.sql, "DROP TABLE backup_blobs;", params![])?; if all_files_extracted {
sql::try_execute(context, &context.sql, "VACUUM;").ok(); // only delete backup_blobs if all files were successfully extracted
Ok(()) sql::execute(context, &context.sql, "DROP TABLE backup_blobs;", params![])?;
}) sql::try_execute(context, &context.sql, "VACUUM;").ok();
Ok(())
} else {
bail!("received stop signal");
}
}
Err(err) => Err(err.into()),
}
} }
/******************************************************************************* /*******************************************************************************
@@ -553,7 +554,7 @@ fn export_backup(context: &Context, dir: impl AsRef<Path>) -> Result<()> {
}; };
dest_sql.close(context); dest_sql.close(context);
res Ok(res?)
} }
fn add_files_to_export(context: &Context, sql: &Sql) -> Result<()> { fn add_files_to_export(context: &Context, sql: &Sql) -> Result<()> {
@@ -576,16 +577,15 @@ fn add_files_to_export(context: &Context, sql: &Sql) -> Result<()> {
info!(context, "EXPORT: total_files_cnt={}", total_files_cnt); info!(context, "EXPORT: total_files_cnt={}", total_files_cnt);
// scan directory, pass 2: copy files // scan directory, pass 2: copy files
let dir_handle = std::fs::read_dir(&dir)?; let dir_handle = std::fs::read_dir(&dir)?;
sql.prepare( let exported_all_files = sql.prepare(
"INSERT INTO backup_blobs (file_name, file_content) VALUES (?, ?);", "INSERT INTO backup_blobs (file_name, file_content) VALUES (?, ?);",
|mut stmt, _| { |mut stmt, _| {
let mut processed_files_cnt = 0; let mut processed_files_cnt = 0;
for entry in dir_handle { for entry in dir_handle {
let entry = entry?; let entry = entry?;
ensure!( if context.shall_stop_ongoing() {
!context.shall_stop_ongoing(), return Ok(false);
"canceled during export-files" }
);
processed_files_cnt += 1; processed_files_cnt += 1;
let permille = max(min(processed_files_cnt * 1000 / total_files_cnt, 990), 10); let permille = max(min(processed_files_cnt * 1000 / total_files_cnt, 990), 10);
context.call_cb(Event::ImexProgress(permille)); context.call_cb(Event::ImexProgress(permille));
@@ -605,9 +605,10 @@ fn add_files_to_export(context: &Context, sql: &Sql) -> Result<()> {
stmt.execute(params![name, buf])?; stmt.execute(params![name, buf])?;
} }
} }
Ok(()) Ok(true)
}, },
)?; )?;
ensure!(exported_all_files, "canceled during export-files");
Ok(()) Ok(())
} }

View File

@@ -534,7 +534,7 @@ pub fn save(
} }
Ok(newest_location_id) Ok(newest_location_id)
}, },
) ).map_err(Into::into)
} }
#[allow(non_snake_case)] #[allow(non_snake_case)]

View File

@@ -266,7 +266,7 @@ impl Message {
Ok(msg) Ok(msg)
}, },
) ).map_err(Into::into)
} }
pub fn delete_from_db(context: &Context, msg_id: MsgId) { pub fn delete_from_db(context: &Context, msg_id: MsgId) {
@@ -1256,7 +1256,7 @@ pub(crate) fn rfc724_mid_exists(
Ok((server_folder, server_uid, msg_id)) Ok((server_folder, server_uid, msg_id))
}, },
) ).map_err(Into::into)
} }
pub fn update_server_uid( pub fn update_server_uid(

View File

@@ -8,7 +8,6 @@ use crate::aheader::*;
use crate::chat::*; use crate::chat::*;
use crate::constants::*; use crate::constants::*;
use crate::context::Context; use crate::context::Context;
use crate::error::*;
use crate::key::*; use crate::key::*;
use crate::sql::{self, Sql}; use crate::sql::{self, Sql};
@@ -385,7 +384,7 @@ impl<'a> Peerstate<'a> {
} }
} }
pub fn save_to_db(&self, sql: &Sql, create: bool) -> Result<()> { pub fn save_to_db(&self, sql: &Sql, create: bool) -> crate::sql::Result<()> {
if create { if create {
sql::execute( sql::execute(
self.context, self.context,

View File

@@ -1,5 +1,7 @@
//! # SQLite wrapper //! # SQLite wrapper
use failure::Fail;
use std::collections::HashSet; use std::collections::HashSet;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use std::time::Duration; use std::time::Duration;
@@ -11,10 +13,53 @@ use crate::chat::update_saved_messages_icon;
use crate::constants::ShowEmails; use crate::constants::ShowEmails;
use crate::context::Context; use crate::context::Context;
use crate::dc_tools::*; use crate::dc_tools::*;
use crate::error::{Error, Result};
use crate::param::*; use crate::param::*;
use crate::peerstate::*; use crate::peerstate::*;
#[derive(Debug, Fail)]
pub enum Error {
#[fail(display = "Sqlite Error: {:?}", _0)]
Sql(#[cause] rusqlite::Error),
#[fail(display = "Sqlite Connection Pool Error: {:?}", _0)]
ConnectionPool(#[cause] r2d2::Error),
#[fail(display = "Sqlite: Connection closed")]
SqlNoConnection,
#[fail(display = "Sqlite: Already open")]
SqlAlreadyOpen,
#[fail(display = "Sqlite: Failed to open")]
SqlFailedToOpen,
#[fail(display = "{:?}", _0)]
Io(#[cause] std::io::Error),
#[fail(display = "{:?}", _0)]
BlobError(#[cause] crate::blob::BlobError),
}
pub type Result<T> = std::result::Result<T, Error>;
impl From<rusqlite::Error> for Error {
fn from(err: rusqlite::Error) -> Error {
Error::Sql(err)
}
}
impl From<r2d2::Error> for Error {
fn from(err: r2d2::Error) -> Error {
Error::ConnectionPool(err)
}
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Error {
Error::Io(err)
}
}
impl From<crate::blob::BlobError> for Error {
fn from(err: crate::blob::BlobError) -> Error {
Error::BlobError(err)
}
}
/// A wrapper around the underlying Sqlite3 object. /// A wrapper around the underlying Sqlite3 object.
#[derive(DebugStub)] #[derive(DebugStub)]
pub struct Sql { pub struct Sql {
@@ -47,7 +92,7 @@ impl Sql {
pub fn open(&self, context: &Context, dbfile: &std::path::Path, readonly: bool) -> bool { pub fn open(&self, context: &Context, dbfile: &std::path::Path, readonly: bool) -> bool {
match open(context, self, dbfile, readonly) { match open(context, self, dbfile, readonly) {
Ok(_) => true, Ok(_) => true,
Err(Error::SqlAlreadyOpen) => false, Err(crate::error::Error::SqlError(Error::SqlAlreadyOpen)) => false,
Err(_) => { Err(_) => {
self.close(context); self.close(context);
false false
@@ -321,14 +366,14 @@ fn open(
sql: &Sql, sql: &Sql,
dbfile: impl AsRef<std::path::Path>, dbfile: impl AsRef<std::path::Path>,
readonly: bool, readonly: bool,
) -> Result<()> { ) -> crate::error::Result<()> {
if sql.is_open() { if sql.is_open() {
error!( error!(
context, context,
"Cannot open, database \"{:?}\" already opened.", "Cannot open, database \"{:?}\" already opened.",
dbfile.as_ref(), dbfile.as_ref(),
); );
return Err(Error::SqlAlreadyOpen); return Err(Error::SqlAlreadyOpen.into());
} }
let mut open_flags = OpenFlags::SQLITE_OPEN_NO_MUTEX; let mut open_flags = OpenFlags::SQLITE_OPEN_NO_MUTEX;
@@ -345,7 +390,8 @@ fn open(
.min_idle(Some(2)) .min_idle(Some(2))
.max_size(10) .max_size(10)
.connection_timeout(std::time::Duration::new(60, 0)) .connection_timeout(std::time::Duration::new(60, 0))
.build(mgr)?; .build(mgr)
.map_err(Error::ConnectionPool)?;
{ {
*sql.pool.write().unwrap() = Some(pool); *sql.pool.write().unwrap() = Some(pool);
@@ -477,7 +523,7 @@ fn open(
dbfile.as_ref(), dbfile.as_ref(),
); );
// cannot create the tables - maybe we cannot write? // cannot create the tables - maybe we cannot write?
return Err(Error::SqlFailedToOpen); return Err(Error::SqlFailedToOpen.into());
} else { } else {
sql.set_raw_config_int(context, "dbversion", 0)?; sql.set_raw_config_int(context, "dbversion", 0)?;
} }