Merge remote-tracking branch 'jgallagher/master' into unlock-notify

This commit is contained in:
gwenn 2018-05-04 18:06:41 +02:00
commit fee4bfcc86
9 changed files with 79 additions and 20 deletions

View File

@ -39,8 +39,8 @@ serde_json = { version = "1.0", optional = true }
[dev-dependencies] [dev-dependencies]
tempdir = "0.3" tempdir = "0.3"
lazy_static = "0.2" lazy_static = "1.0"
regex = "0.2" regex = "1.0"
[dependencies.libsqlite3-sys] [dependencies.libsqlite3-sys]
path = "libsqlite3-sys" path = "libsqlite3-sys"

View File

@ -1,8 +1,8 @@
environment: environment:
matrix: matrix:
- TARGET: 1.21.0-x86_64-pc-windows-gnu - TARGET: 1.24.1-x86_64-pc-windows-gnu
MSYS2_BITS: 64 MSYS2_BITS: 64
- TARGET: 1.21.0-x86_64-pc-windows-msvc - TARGET: 1.24.1-x86_64-pc-windows-msvc
VCPKG_DEFAULT_TRIPLET: x64-windows VCPKG_DEFAULT_TRIPLET: x64-windows
VCPKGRS_DYNAMIC: 1 VCPKGRS_DYNAMIC: 1
- TARGET: nightly-x86_64-pc-windows-msvc - TARGET: nightly-x86_64-pc-windows-msvc

View File

@ -25,7 +25,7 @@ min_sqlite_version_3_7_16 = ["pkg-config", "vcpkg"]
unlock_notify = [] unlock_notify = []
[build-dependencies] [build-dependencies]
bindgen = { version = "0.32", optional = true } bindgen = { version = "0.36", optional = true }
pkg-config = { version = "0.3", optional = true } pkg-config = { version = "0.3", optional = true }
cc = { version = "1.0", optional = true } cc = { version = "1.0", optional = true }

View File

@ -46,6 +46,7 @@ impl Connection {
self.cache.set_capacity(capacity) self.cache.set_capacity(capacity)
} }
/// Remove/finalize all prepared statements currently in the cache.
pub fn flush_prepared_statement_cache(&self) { pub fn flush_prepared_statement_cache(&self) {
self.cache.flush() self.cache.flush()
} }
@ -124,7 +125,7 @@ impl StatementCache {
sql: &str) sql: &str)
-> Result<CachedStatement<'conn>> { -> Result<CachedStatement<'conn>> {
let mut cache = self.0.borrow_mut(); let mut cache = self.0.borrow_mut();
let stmt = match cache.remove(sql) { let stmt = match cache.remove(sql.trim()) {
Some(raw_stmt) => Ok(Statement::new(conn, raw_stmt)), Some(raw_stmt) => Ok(Statement::new(conn, raw_stmt)),
None => conn.prepare(sql), None => conn.prepare(sql),
}; };
@ -135,7 +136,7 @@ impl StatementCache {
fn cache_stmt(&self, stmt: RawStatement) { fn cache_stmt(&self, stmt: RawStatement) {
let mut cache = self.0.borrow_mut(); let mut cache = self.0.borrow_mut();
stmt.clear_bindings(); stmt.clear_bindings();
let sql = String::from_utf8_lossy(stmt.sql().to_bytes()).to_string(); let sql = String::from_utf8_lossy(stmt.sql().to_bytes()).trim().to_string();
cache.insert(sql, stmt); cache.insert(sql, stmt);
} }
@ -285,4 +286,27 @@ mod test {
conn.close().expect("connection not closed"); conn.close().expect("connection not closed");
} }
#[test]
fn test_cache_key() {
let db = Connection::open_in_memory().unwrap();
let cache = &db.cache;
assert_eq!(0, cache.len());
//let sql = " PRAGMA schema_version; -- comment";
let sql = "PRAGMA schema_version; ";
{
let mut stmt = db.prepare_cached(sql).unwrap();
assert_eq!(0, cache.len());
assert_eq!(0, stmt.query_row(&[], |r| r.get::<i32, i64>(0)).unwrap());
}
assert_eq!(1, cache.len());
{
let mut stmt = db.prepare_cached(sql).unwrap();
assert_eq!(0, cache.len());
assert_eq!(0, stmt.query_row(&[], |r| r.get::<i32, i64>(0)).unwrap());
}
assert_eq!(1, cache.len());
}
} }

View File

@ -200,7 +200,7 @@ impl Connection {
/// Open a new connection to a SQLite database. /// Open a new connection to a SQLite database.
/// ///
/// `Connection::open(path)` is equivalent to `Connection::open_with_flags(path, /// `Connection::open(path)` is equivalent to `Connection::open_with_flags(path,
/// SQLITE_OPEN_READ_WRITE | SQLITE_OPEN_CREATE)`. /// OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE)`.
/// ///
/// # Failure /// # Failure
/// ///

View File

@ -45,7 +45,7 @@ impl<'conn> Statement<'conn> {
let bytes = name.as_bytes(); let bytes = name.as_bytes();
let n = self.column_count(); let n = self.column_count();
for i in 0..n { for i in 0..n {
if bytes == self.stmt.column_name(i).to_bytes() { if bytes.eq_ignore_ascii_case(self.stmt.column_name(i).to_bytes()) {
return Ok(i); return Ok(i);
} }
} }
@ -785,4 +785,30 @@ mod test {
let y: Result<i64> = stmt.query_row(&[&1i32], |r| r.get(0)); let y: Result<i64> = stmt.query_row(&[&1i32], |r| r.get(0));
assert_eq!(3i64, y.unwrap()); assert_eq!(3i64, y.unwrap());
} }
#[test]
fn test_query_by_column_name() {
let db = Connection::open_in_memory().unwrap();
let sql = "BEGIN;
CREATE TABLE foo(x INTEGER, y INTEGER);
INSERT INTO foo VALUES(1, 3);
END;";
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT y FROM foo").unwrap();
let y: Result<i64> = stmt.query_row(&[], |r| r.get("y"));
assert_eq!(3i64, y.unwrap());
}
#[test]
fn test_query_by_column_name_ignore_case() {
let db = Connection::open_in_memory().unwrap();
let sql = "BEGIN;
CREATE TABLE foo(x INTEGER, y INTEGER);
INSERT INTO foo VALUES(1, 3);
END;";
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT y as Y FROM foo").unwrap();
let y: Result<i64> = stmt.query_row(&[], |r| r.get("y"));
assert_eq!(3i64, y.unwrap());
}
} }

View File

@ -26,6 +26,9 @@ pub enum DropBehavior {
/// Do not commit or roll back changes - this will leave the transaction or savepoint /// Do not commit or roll back changes - this will leave the transaction or savepoint
/// open, so should be used with care. /// open, so should be used with care.
Ignore, Ignore,
/// Panic. Used to enforce intentional behavior during development.
Panic,
} }
/// Old name for `Transaction`. `SqliteTransaction` is deprecated. /// Old name for `Transaction`. `SqliteTransaction` is deprecated.
@ -195,6 +198,7 @@ impl<'conn> Transaction<'conn> {
DropBehavior::Commit => self.commit_(), DropBehavior::Commit => self.commit_(),
DropBehavior::Rollback => self.rollback_(), DropBehavior::Rollback => self.rollback_(),
DropBehavior::Ignore => Ok(()), DropBehavior::Ignore => Ok(()),
DropBehavior::Panic => panic!("Transaction dropped unexpectedly."),
} }
} }
} }
@ -306,6 +310,7 @@ impl<'conn> Savepoint<'conn> {
DropBehavior::Commit => self.commit_(), DropBehavior::Commit => self.commit_(),
DropBehavior::Rollback => self.rollback(), DropBehavior::Rollback => self.rollback(),
DropBehavior::Ignore => Ok(()), DropBehavior::Ignore => Ok(()),
DropBehavior::Panic => panic!("Savepoint dropped unexpectedly."),
} }
} }
} }

View File

@ -10,12 +10,13 @@
//! * Strings (`String` and `&str`) //! * Strings (`String` and `&str`)
//! * Blobs (`Vec<u8>` and `&[u8]`) //! * Blobs (`Vec<u8>` and `&[u8]`)
//! //!
//! Additionally, because it is such a common data type, implementations are provided for //! Additionally, because it is such a common data type, implementations are
//! `time::Timespec` that use a string for storage (using the same format string, //! provided for `time::Timespec` that use the RFC 3339 date/time format,
//! `"%Y-%m-%d %H:%M:%S"`, as SQLite's builtin //! `"%Y-%m-%dT%H:%M:%S.%fZ"`, to store time values as strings. These values
//! [datetime](https://www.sqlite.org/lang_datefunc.html) function. Note that this storage //! can be parsed by SQLite's builtin
//! truncates timespecs to the nearest second. If you want different storage for timespecs, you can //! [datetime](https://www.sqlite.org/lang_datefunc.html) functions. If you
//! use a newtype. For example, to store timespecs as `f64`s: //! want different storage for timespecs, you can use a newtype. For example, to
//! store timespecs as `f64`s:
//! //!
//! ```rust //! ```rust
//! extern crate rusqlite; //! extern crate rusqlite;

View File

@ -3,7 +3,8 @@ extern crate time;
use Result; use Result;
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef}; use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
const SQLITE_DATETIME_FMT: &str = "%Y-%m-%d %H:%M:%S:%f %Z"; const SQLITE_DATETIME_FMT: &str = "%Y-%m-%dT%H:%M:%S.%fZ";
const SQLITE_DATETIME_FMT_LEGACY: &str = "%Y-%m-%d %H:%M:%S:%f %Z";
impl ToSql for time::Timespec { impl ToSql for time::Timespec {
fn to_sql(&self) -> Result<ToSqlOutput> { fn to_sql(&self) -> Result<ToSqlOutput> {
@ -19,10 +20,12 @@ impl FromSql for time::Timespec {
fn column_result(value: ValueRef) -> FromSqlResult<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
value value
.as_str() .as_str()
.and_then(|s| match time::strptime(s, SQLITE_DATETIME_FMT) { .and_then(|s| {
Ok(tm) => Ok(tm.to_timespec()), time::strptime(s, SQLITE_DATETIME_FMT)
Err(err) => Err(FromSqlError::Other(Box::new(err))), .or_else(|err| {
}) time::strptime(s, SQLITE_DATETIME_FMT_LEGACY)
.or(Err(FromSqlError::Other(Box::new(err))))})})
.map(|tm| tm.to_timespec())
} }
} }