diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 6d0dd29..df22189 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -1,5 +1,5 @@ -rusqlite contributors (sorted alphabetically) -============================================= +rusqlite contributors +===================== * [John Gallagher](https://github.com/jgallagher) * [Marcus Klaas de Vries](https://github.com/marcusklaas) @@ -13,3 +13,4 @@ rusqlite contributors (sorted alphabetically) * [Andrew Straw](https://github.com/astraw) * [Ronald Kinard](https://github.com/Furyhunter) * [maciejkula](https://github.com/maciejkula) +* [Xidorn Quan](https://github.com/upsuper) diff --git a/Cargo.toml b/Cargo.toml index 23c3cd6..8fb06d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ license = "MIT" name = "rusqlite" [features] -load_extension = ["libsqlite3-sys/load_extension"] +load_extension = [] backup = [] blob = [] functions = [] @@ -23,6 +23,7 @@ trace = [] time = "~0.1.0" bitflags = "~0.1" libc = "~0.2" +clippy = {version = "~0.0.58", optional = true} chrono = { version = "~0.2", optional = true } serde_json = { version = "0.6", optional = true } diff --git a/Changelog.md b/Changelog.md index 63828da..ad5ca25 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,9 @@ # Version UPCOMING (...) +* Removes `load_extension` feature from `libsqlite3-sys`. `load_extension` is still available + on rusqlite itself. +* Fixes crash on nightly Rust when using the `trace` feature. +* Adds optional `clippy` feature and addresses issues it found. * Adds `column_count()` method to `Statement` and `Row`. * Adds `types::Value` for dynamic column types. * Adds support for user-defined aggregate functions (behind the existing `functions` Cargo feature). diff --git a/libsqlite3-sys/Cargo.toml b/libsqlite3-sys/Cargo.toml index b199cfc..b93650b 100644 --- a/libsqlite3-sys/Cargo.toml +++ b/libsqlite3-sys/Cargo.toml @@ -8,9 +8,6 @@ license = "MIT" links = "sqlite3" build = "build.rs" -[features] -load_extension = [] - [build-dependencies] pkg-config = "~0.3" diff --git a/src/error.rs b/src/error.rs index 3f356f6..cc753c7 100644 --- a/src/error.rs +++ b/src/error.rs @@ -82,84 +82,87 @@ impl From<::std::ffi::NulError> for Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &Error::SqliteFailure(ref err, None) => err.fmt(f), - &Error::SqliteFailure(_, Some(ref s)) => write!(f, "{}", s), - &Error::SqliteSingleThreadedMode => { + match *self { + Error::SqliteFailure(ref err, None) => err.fmt(f), + Error::SqliteFailure(_, Some(ref s)) => write!(f, "{}", s), + Error::SqliteSingleThreadedMode => { write!(f, "SQLite was compiled or configured for single-threaded use only") } - &Error::FromSqlConversionFailure(ref err) => err.fmt(f), - &Error::Utf8Error(ref err) => err.fmt(f), - &Error::NulError(ref err) => err.fmt(f), - &Error::InvalidParameterName(ref name) => write!(f, "Invalid parameter name: {}", name), - &Error::InvalidPath(ref p) => write!(f, "Invalid path: {}", p.to_string_lossy()), - &Error::ExecuteReturnedResults => { + Error::FromSqlConversionFailure(ref err) => err.fmt(f), + Error::Utf8Error(ref err) => err.fmt(f), + Error::NulError(ref err) => err.fmt(f), + Error::InvalidParameterName(ref name) => write!(f, "Invalid parameter name: {}", name), + Error::InvalidPath(ref p) => write!(f, "Invalid path: {}", p.to_string_lossy()), + Error::ExecuteReturnedResults => { write!(f, "Execute returned results - did you mean to call query?") } - &Error::QueryReturnedNoRows => write!(f, "Query returned no rows"), - &Error::GetFromStaleRow => write!(f, "Attempted to get a value from a stale row"), - &Error::InvalidColumnIndex(i) => write!(f, "Invalid column index: {}", i), - &Error::InvalidColumnName(ref name) => write!(f, "Invalid column name: {}", name), - &Error::InvalidColumnType => write!(f, "Invalid column type"), + Error::QueryReturnedNoRows => write!(f, "Query returned no rows"), + Error::GetFromStaleRow => write!(f, "Attempted to get a value from a stale row"), + Error::InvalidColumnIndex(i) => write!(f, "Invalid column index: {}", i), + Error::InvalidColumnName(ref name) => write!(f, "Invalid column name: {}", name), + Error::InvalidColumnType => write!(f, "Invalid column type"), #[cfg(feature = "functions")] - &Error::InvalidFunctionParameterType => write!(f, "Invalid function parameter type"), + Error::InvalidFunctionParameterType => write!(f, "Invalid function parameter type"), #[cfg(feature = "functions")] - &Error::UserFunctionError(ref err) => err.fmt(f), + Error::UserFunctionError(ref err) => err.fmt(f), } } } impl error::Error for Error { fn description(&self) -> &str { - match self { - &Error::SqliteFailure(ref err, None) => err.description(), - &Error::SqliteFailure(_, Some(ref s)) => s, - &Error::SqliteSingleThreadedMode => { + match *self { + Error::SqliteFailure(ref err, None) => err.description(), + Error::SqliteFailure(_, Some(ref s)) => s, + Error::SqliteSingleThreadedMode => { "SQLite was compiled or configured for single-threaded use only" } - &Error::FromSqlConversionFailure(ref err) => err.description(), - &Error::Utf8Error(ref err) => err.description(), - &Error::InvalidParameterName(_) => "invalid parameter name", - &Error::NulError(ref err) => err.description(), - &Error::InvalidPath(_) => "invalid path", - &Error::ExecuteReturnedResults => { + Error::FromSqlConversionFailure(ref err) => err.description(), + Error::Utf8Error(ref err) => err.description(), + Error::InvalidParameterName(_) => "invalid parameter name", + Error::NulError(ref err) => err.description(), + Error::InvalidPath(_) => "invalid path", + Error::ExecuteReturnedResults => { "execute returned results - did you mean to call query?" } - &Error::QueryReturnedNoRows => "query returned no rows", - &Error::GetFromStaleRow => "attempted to get a value from a stale row", - &Error::InvalidColumnIndex(_) => "invalid column index", - &Error::InvalidColumnName(_) => "invalid column name", - &Error::InvalidColumnType => "invalid column type", + Error::QueryReturnedNoRows => "query returned no rows", + Error::GetFromStaleRow => "attempted to get a value from a stale row", + Error::InvalidColumnIndex(_) => "invalid column index", + Error::InvalidColumnName(_) => "invalid column name", + Error::InvalidColumnType => "invalid column type", #[cfg(feature = "functions")] - &Error::InvalidFunctionParameterType => "invalid function parameter type", + Error::InvalidFunctionParameterType => "invalid function parameter type", #[cfg(feature = "functions")] - &Error::UserFunctionError(ref err) => err.description(), + Error::UserFunctionError(ref err) => err.description(), } } + #[cfg_attr(feature="clippy", allow(match_same_arms))] fn cause(&self) -> Option<&error::Error> { - match self { - &Error::SqliteFailure(ref err, _) => Some(err), - &Error::SqliteSingleThreadedMode => None, - &Error::FromSqlConversionFailure(ref err) => Some(&**err), - &Error::Utf8Error(ref err) => Some(err), - &Error::NulError(ref err) => Some(err), - &Error::InvalidParameterName(_) => None, - &Error::InvalidPath(_) => None, - &Error::ExecuteReturnedResults => None, - &Error::QueryReturnedNoRows => None, - &Error::GetFromStaleRow => None, - &Error::InvalidColumnIndex(_) => None, - &Error::InvalidColumnName(_) => None, - &Error::InvalidColumnType => None, + match *self { + Error::SqliteFailure(ref err, _) => Some(err), + Error::FromSqlConversionFailure(ref err) => Some(&**err), + Error::Utf8Error(ref err) => Some(err), + Error::NulError(ref err) => Some(err), + + Error::SqliteSingleThreadedMode | + Error::InvalidParameterName(_) | + Error::ExecuteReturnedResults | + Error::QueryReturnedNoRows | + Error::GetFromStaleRow | + Error::InvalidColumnIndex(_) | + Error::InvalidColumnName(_) | + Error::InvalidColumnType | + Error::InvalidPath(_) => None, #[cfg(feature = "functions")] - &Error::InvalidFunctionParameterType => None, + Error::InvalidFunctionParameterType => None, + #[cfg(feature = "functions")] - &Error::UserFunctionError(ref err) => Some(&**err), + Error::UserFunctionError(ref err) => Some(&**err), } } } diff --git a/src/functions.rs b/src/functions.rs index 49e0032..8fd78f0 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -88,9 +88,10 @@ raw_to_impl!(c_double, sqlite3_result_double); impl<'a> ToResult for bool { unsafe fn set_result(&self, ctx: *mut sqlite3_context) { - match *self { - true => ffi::sqlite3_result_int(ctx, 1), - _ => ffi::sqlite3_result_int(ctx, 0), + if *self { + ffi::sqlite3_result_int(ctx, 1) + } else { + ffi::sqlite3_result_int(ctx, 0) } } } @@ -214,7 +215,7 @@ impl FromValue for String { unsafe fn parameter_value(v: *mut sqlite3_value) -> Result { let c_text = ffi::sqlite3_value_text(v); if c_text.is_null() { - Ok("".to_string()) + Ok("".to_owned()) } else { let c_slice = CStr::from_ptr(c_text as *const c_char).to_bytes(); let utf8_str = try!(str::from_utf8(c_slice)); @@ -250,7 +251,7 @@ impl FromValue for Option { if sqlite3_value_type(v) == ffi::SQLITE_NULL { Ok(None) } else { - FromValue::parameter_value(v).map(|t| Some(t)) + FromValue::parameter_value(v).map(Some) } } @@ -274,6 +275,10 @@ impl<'a> Context<'a> { pub fn len(&self) -> usize { self.args.len() } + /// Returns `true` when there is no argument. + pub fn is_empty(&self) -> bool { + self.args.is_empty() + } /// Returns the `idx`th argument as a `T`. /// @@ -302,7 +307,7 @@ impl<'a> Context<'a> { ffi::sqlite3_set_auxdata(self.ctx, arg, mem::transmute(boxed), - Some(mem::transmute(free_boxed_value::))) + Some(free_boxed_value::)) }; } @@ -475,7 +480,7 @@ impl InnerConnection { Some(call_boxed_closure::), None, None, - Some(mem::transmute(free_boxed_value::))) + Some(free_boxed_value::)) }; self.decode_result(r) } @@ -592,7 +597,7 @@ impl InnerConnection { None, Some(call_boxed_step::), Some(call_boxed_final::), - Some(mem::transmute(free_boxed_value::))) + Some(free_boxed_value::)) }; self.decode_result(r) } @@ -621,6 +626,7 @@ mod test { use std::collections::HashMap; use libc::c_double; use self::regex::Regex; + use std::f64::EPSILON; use {Connection, Error, Result}; use functions::{Aggregate, Context}; @@ -637,7 +643,7 @@ mod test { db.create_scalar_function("half", 1, true, half).unwrap(); let result: Result = db.query_row("SELECT half(6)", &[], |r| r.get(0)); - assert_eq!(3f64, result.unwrap()); + assert!((3f64 - result.unwrap()).abs() < EPSILON); } #[test] @@ -645,7 +651,7 @@ mod test { let db = Connection::open_in_memory().unwrap(); db.create_scalar_function("half", 1, true, half).unwrap(); let result: Result = db.query_row("SELECT half(6)", &[], |r| r.get(0)); - assert_eq!(3f64, result.unwrap()); + assert!((3f64 - result.unwrap()).abs() < EPSILON); db.remove_function("half", 1).unwrap(); let result: Result = db.query_row("SELECT half(6)", &[], |r| r.get(0)); diff --git a/src/lib.rs b/src/lib.rs index f81926b..930e1c6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,6 +50,9 @@ //! } //! } //! ``` +#![cfg_attr(feature="clippy", feature(plugin))] +#![cfg_attr(feature="clippy", plugin(clippy))] + extern crate libc; extern crate libsqlite3_sys as ffi; #[macro_use] @@ -99,7 +102,7 @@ pub type Result = result::Result; unsafe fn errmsg_to_string(errmsg: *const c_char) -> String { let c_slice = CStr::from_ptr(errmsg).to_bytes(); let utf8_str = str::from_utf8(c_slice); - utf8_str.unwrap_or("Invalid string encoding").to_string() + utf8_str.unwrap_or("Invalid string encoding").to_owned() } fn str_to_cstring(s: &str) -> Result { @@ -127,9 +130,9 @@ pub enum DatabaseName<'a> { // impl to avoid dead code warnings. #[cfg(any(feature = "backup", feature = "blob"))] impl<'a> DatabaseName<'a> { - fn to_cstring(self) -> Result { + fn to_cstring(&self) -> Result { use self::DatabaseName::{Main, Temp, Attached}; - match self { + match *self { Main => str_to_cstring("main"), Temp => str_to_cstring("temp"), Attached(s) => str_to_cstring(s), @@ -234,7 +237,7 @@ impl Connection { /// # Failure /// /// Will return `Err` if the underlying SQLite call fails. - pub fn transaction<'a>(&'a self) -> Result> { + pub fn transaction(&self) -> Result { Transaction::new(self, TransactionBehavior::Deferred) } @@ -245,9 +248,7 @@ impl Connection { /// # Failure /// /// Will return `Err` if the underlying SQLite call fails. - pub fn transaction_with_behavior<'a>(&'a self, - behavior: TransactionBehavior) - -> Result> { + pub fn transaction_with_behavior(&self, behavior: TransactionBehavior) -> Result { Transaction::new(self, behavior) } @@ -572,11 +573,7 @@ impl InnerConnection { // https://github.com/mackyle/sqlite/blob/master/src/mutex_noop.c). const SQLITE_SINGLETHREADED_MUTEX_MAGIC: usize = 8; let mutex_ptr = ffi::sqlite3_mutex_alloc(0); - let is_singlethreaded = if mutex_ptr as usize == SQLITE_SINGLETHREADED_MUTEX_MAGIC { - true - } else { - false - }; + let is_singlethreaded = mutex_ptr as usize == SQLITE_SINGLETHREADED_MUTEX_MAGIC; ffi::sqlite3_mutex_free(mutex_ptr); if is_singlethreaded { return Err(Error::SqliteSingleThreadedMode); @@ -794,10 +791,10 @@ impl<'conn> Statement<'conn> { ffi::sqlite3_reset(self.stmt); match r { ffi::SQLITE_DONE => { - if self.column_count != 0 { - Err(Error::ExecuteReturnedResults) - } else { + if self.column_count == 0 { Ok(self.conn.changes()) + } else { + Err(Error::ExecuteReturnedResults) } } ffi::SQLITE_ROW => Err(Error::ExecuteReturnedResults), @@ -1173,9 +1170,9 @@ impl<'a> RowIndex for &'a str { #[cfg(test)] mod test { - extern crate libsqlite3_sys as ffi; extern crate tempdir; pub use super::*; + use ffi; use self::tempdir::TempDir; pub use std::error::Error as StdError; pub use std::fmt; @@ -1224,10 +1221,9 @@ mod test { #[test] fn test_open_with_flags() { - for bad_flags in [OpenFlags::empty(), - SQLITE_OPEN_READ_ONLY | SQLITE_OPEN_READ_WRITE, - SQLITE_OPEN_READ_ONLY | SQLITE_OPEN_CREATE] - .iter() { + for bad_flags in &[OpenFlags::empty(), + SQLITE_OPEN_READ_ONLY | SQLITE_OPEN_READ_WRITE, + SQLITE_OPEN_READ_ONLY | SQLITE_OPEN_CREATE] { assert!(Connection::open_in_memory_with_flags(*bad_flags).is_err()); } } diff --git a/src/trace.rs b/src/trace.rs index bd3b537..a36aa33 100644 --- a/src/trace.rs +++ b/src/trace.rs @@ -35,7 +35,9 @@ pub unsafe fn config_log(callback: Option) -> Result<()> { let rc = match callback { Some(f) => { let p_arg: *mut c_void = mem::transmute(f); - ffi::sqlite3_config(ffi::SQLITE_CONFIG_LOG, Some(log_callback), p_arg) + ffi::sqlite3_config(ffi::SQLITE_CONFIG_LOG, + log_callback as extern "C" fn(_, _, _), + p_arg) } None => { let nullptr: *mut c_void = ptr::null_mut(); diff --git a/src/transaction.rs b/src/transaction.rs index ea09cef..e72add4 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -89,7 +89,7 @@ impl<'conn> Transaction<'conn> { /// tx.commit() /// } /// ``` - pub fn savepoint<'a>(&'a self) -> Result> { + pub fn savepoint(&self) -> Result { self.conn.execute_batch("SAVEPOINT sp").map(|_| { Transaction { conn: self.conn, @@ -174,6 +174,7 @@ impl<'conn> Drop for Transaction<'conn> { } #[cfg(test)] +#[cfg_attr(feature="clippy", allow(similar_names))] mod test { use Connection; diff --git a/src/types/mod.rs b/src/types/mod.rs index 8c60f31..51648a9 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -104,9 +104,10 @@ raw_to_impl!(c_double, sqlite3_bind_double); impl ToSql for bool { unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int { - match *self { - true => ffi::sqlite3_bind_int(stmt, col, 1), - _ => ffi::sqlite3_bind_int(stmt, col, 0), + if *self { + ffi::sqlite3_bind_int(stmt, col, 1) + } else { + ffi::sqlite3_bind_int(stmt, col, 0) } } } @@ -224,7 +225,7 @@ impl FromSql for String { unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> Result { let c_text = ffi::sqlite3_column_text(stmt, col); if c_text.is_null() { - Ok("".to_string()) + Ok("".to_owned()) } else { let c_slice = CStr::from_ptr(c_text as *const c_char).to_bytes(); let utf8_str = try!(str::from_utf8(c_slice)); @@ -262,7 +263,7 @@ impl FromSql for Option { if sqlite3_column_type(stmt, col) == ffi::SQLITE_NULL { Ok(None) } else { - FromSql::column_result(stmt, col).map(|t| Some(t)) + FromSql::column_result(stmt, col).map(Some) } } @@ -291,11 +292,11 @@ pub enum Value { impl FromSql for Value { unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> Result { match sqlite3_column_type(stmt, col) { - ffi::SQLITE_TEXT => FromSql::column_result(stmt, col).map(|t| Value::Text(t)), + ffi::SQLITE_TEXT => FromSql::column_result(stmt, col).map(Value::Text), ffi::SQLITE_INTEGER => Ok(Value::Integer(ffi::sqlite3_column_int64(stmt, col))), ffi::SQLITE_FLOAT => Ok(Value::Real(ffi::sqlite3_column_double(stmt, col))), ffi::SQLITE_NULL => Ok(Value::Null), - ffi::SQLITE_BLOB => FromSql::column_result(stmt, col).map(|t| Value::Blob(t)), + ffi::SQLITE_BLOB => FromSql::column_result(stmt, col).map(Value::Blob), _ => Err(Error::InvalidColumnType), } } @@ -306,12 +307,14 @@ impl FromSql for Value { } #[cfg(test)] +#[cfg_attr(feature="clippy", allow(similar_names))] mod test { extern crate time; use Connection; use Error; use libc::{c_int, c_double}; + use std::f64::EPSILON; fn checked_memory_handle() -> Connection { let db = Connection::open_in_memory().unwrap(); @@ -335,7 +338,7 @@ mod test { let db = checked_memory_handle(); let s = "hello, world!"; - db.execute("INSERT INTO foo(t) VALUES (?)", &[&s.to_string()]).unwrap(); + db.execute("INSERT INTO foo(t) VALUES (?)", &[&s.to_owned()]).unwrap(); let from: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); assert_eq!(from, s); @@ -368,6 +371,7 @@ mod test { } #[test] + #[cfg_attr(feature="clippy", allow(cyclomatic_complexity))] fn test_mismatched_types() { fn is_invalid_column_type(err: Error) -> bool { match err { @@ -391,7 +395,7 @@ mod test { assert_eq!(vec![1, 2], row.get_checked::>(0).unwrap()); assert_eq!("text", row.get_checked::(1).unwrap()); assert_eq!(1, row.get_checked::(2).unwrap()); - assert_eq!(1.5, row.get_checked::(3).unwrap()); + assert!((1.5 - row.get_checked::(3).unwrap()).abs() < EPSILON); assert!(row.get_checked::>(4).unwrap().is_none()); assert!(row.get_checked::>(4).unwrap().is_none()); assert!(row.get_checked::>(4).unwrap().is_none()); @@ -454,7 +458,10 @@ mod test { assert_eq!(Value::Text(String::from("text")), row.get_checked::(1).unwrap()); assert_eq!(Value::Integer(1), row.get_checked::(2).unwrap()); - assert_eq!(Value::Real(1.5), row.get_checked::(3).unwrap()); + match row.get_checked::(3).unwrap() { + Value::Real(val) => assert!((1.5 - val).abs() < EPSILON), + x => panic!("Invalid Value {:?}", x), + } assert_eq!(Value::Null, row.get_checked::(4).unwrap()); } }