Merge remote-tracking branch 'jgallagher/master' into vtab

This commit is contained in:
gwenn
2017-03-08 20:35:07 +01:00
49 changed files with 25702 additions and 9488 deletions

View File

@@ -30,7 +30,7 @@ use std::marker::PhantomData;
use std::path::Path;
use std::ptr;
use libc::c_int;
use std::os::raw::c_int;
use std::thread;
use std::time::Duration;

View File

@@ -269,7 +269,7 @@ mod test {
let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false).unwrap();
assert_eq!(4, blob.write(b"Clob").unwrap());
assert_eq!(6, blob.write(b"567890xxxxxx").unwrap()); // cannot write past 10
assert_eq!(0, blob.write(b"5678").unwrap()); // still cannot write past 10
assert_eq!(0, blob.write(b"5678").unwrap()); // still cannot write past 10
blob.reopen(rowid).unwrap();
blob.close().unwrap();

View File

@@ -5,6 +5,7 @@ use std::ops::{Deref, DerefMut};
use lru_cache::LruCache;
use {Result, Connection, Statement};
use raw_statement::RawStatement;
use statement::StatementCrateImpl;
impl Connection {
/// Prepare a SQL statement for execution, returning a previously prepared (but
@@ -258,7 +259,11 @@ mod test {
{
let mut stmt = db.prepare_cached(sql).unwrap();
assert_eq!(1i32,
stmt.query_map(&[], |r| r.get(0)).unwrap().next().unwrap().unwrap());
stmt.query_map::<i32, _>(&[], |r| r.get(0))
.unwrap()
.next()
.unwrap()
.unwrap());
}
db.execute_batch(r#"

View File

@@ -1,120 +0,0 @@
use {Error, Result, Row, Statement};
use types::ToSql;
impl<'conn> Statement<'conn> {
/// Execute an INSERT and return the ROWID.
///
/// # Note
///
/// This function is a convenience wrapper around `execute()` intended for queries that
/// insert a single item. It is possible to misuse this function in a way that it cannot
/// detect, such as by calling it on a statement which _updates_ a single item rather than
/// inserting one. Please don't do that.
///
/// # Failure
///
/// Will return `Err` if no row is inserted or many rows are inserted.
pub fn insert(&mut self, params: &[&ToSql]) -> Result<i64> {
let changes = try!(self.execute(params));
match changes {
1 => Ok(self.conn.last_insert_rowid()),
_ => Err(Error::StatementChangedRows(changes)),
}
}
/// Return `true` if a query in the SQL statement it executes returns one or more rows
/// and `false` if the SQL returns an empty set.
pub fn exists(&mut self, params: &[&ToSql]) -> Result<bool> {
let mut rows = try!(self.query(params));
let exists = {
match rows.next() {
Some(_) => true,
None => false,
}
};
Ok(exists)
}
/// Convenience method to execute a query that is expected to return a single row.
///
/// If the query returns more than one row, all rows except the first are ignored.
///
/// # Failure
///
/// Will return `Err` if the underlying SQLite call fails.
pub fn query_row<T, F>(&mut self, params: &[&ToSql], f: F) -> Result<T>
where F: FnOnce(&Row) -> T
{
let mut rows = try!(self.query(params));
rows.get_expected_row().map(|r| f(&r))
}
}
#[cfg(test)]
mod test {
use {Connection, Error, Result};
#[test]
fn test_insert() {
let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo(x INTEGER UNIQUE)").unwrap();
let mut stmt = db.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?)").unwrap();
assert_eq!(stmt.insert(&[&1i32]).unwrap(), 1);
assert_eq!(stmt.insert(&[&2i32]).unwrap(), 2);
match stmt.insert(&[&1i32]).unwrap_err() {
Error::StatementChangedRows(0) => (),
err => panic!("Unexpected error {}", err),
}
let mut multi = db.prepare("INSERT INTO foo (x) SELECT 3 UNION ALL SELECT 4").unwrap();
match multi.insert(&[]).unwrap_err() {
Error::StatementChangedRows(2) => (),
err => panic!("Unexpected error {}", err),
}
}
#[test]
fn test_insert_different_tables() {
// Test for https://github.com/jgallagher/rusqlite/issues/171
let db = Connection::open_in_memory().unwrap();
db.execute_batch(r"
CREATE TABLE foo(x INTEGER);
CREATE TABLE bar(x INTEGER);
")
.unwrap();
assert_eq!(db.prepare("INSERT INTO foo VALUES (10)").unwrap().insert(&[]).unwrap(),
1);
assert_eq!(db.prepare("INSERT INTO bar VALUES (10)").unwrap().insert(&[]).unwrap(),
1);
}
#[test]
fn test_exists() {
let db = Connection::open_in_memory().unwrap();
let sql = "BEGIN;
CREATE TABLE foo(x INTEGER);
INSERT INTO foo VALUES(1);
INSERT INTO foo VALUES(2);
END;";
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT 1 FROM foo WHERE x = ?").unwrap();
assert!(stmt.exists(&[&1i32]).unwrap());
assert!(stmt.exists(&[&2i32]).unwrap());
assert!(!stmt.exists(&[&0i32]).unwrap());
}
#[test]
fn test_query_row() {
let db = Connection::open_in_memory().unwrap();
let sql = "BEGIN;
CREATE TABLE foo(x INTEGER, y INTEGER);
INSERT INTO foo VALUES(1, 3);
INSERT INTO foo VALUES(2, 4);
END;";
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT y FROM foo WHERE x = ?").unwrap();
let y: Result<i64> = stmt.query_row(&[&1i32], |r| r.get(0));
assert_eq!(3i64, y.unwrap());
}
}

View File

@@ -2,7 +2,7 @@ use std::error;
use std::fmt;
use std::path::PathBuf;
use std::str;
use libc::c_int;
use std::os::raw::c_int;
use {ffi, errmsg_to_string};
use types::Type;
@@ -25,6 +25,11 @@ pub enum Error {
/// the requested Rust type.
FromSqlConversionFailure(usize, Type, Box<error::Error + Send + Sync>),
/// Error when SQLite gives us an integral value outside the range of the requested type (e.g.,
/// trying to get the value 1000 into a `u8`). The associated `c_int` is the column index, and
/// the associated `i64` is the value returned by SQLite.
IntegralValueOutOfRange(c_int, i64),
/// Error converting a string to UTF-8.
Utf8Error(str::Utf8Error),
@@ -105,6 +110,9 @@ impl fmt::Display for Error {
i,
err)
}
Error::IntegralValueOutOfRange(col, val) => {
write!(f, "Integer {} out of range at index {}", val, col)
}
Error::Utf8Error(ref err) => err.fmt(f),
Error::NulError(ref err) => err.fmt(f),
Error::InvalidParameterName(ref name) => write!(f, "Invalid parameter name: {}", name),
@@ -141,6 +149,7 @@ impl error::Error for Error {
"SQLite was compiled or configured for single-threaded use only"
}
Error::FromSqlConversionFailure(_, _, ref err) => err.description(),
Error::IntegralValueOutOfRange(_, _) => "integral value out of range of requested type",
Error::Utf8Error(ref err) => err.description(),
Error::InvalidParameterName(_) => "invalid parameter name",
Error::NulError(ref err) => err.description(),
@@ -170,6 +179,7 @@ impl error::Error for Error {
Error::Utf8Error(ref err) => Some(err),
Error::NulError(ref err) => Some(err),
Error::IntegralValueOutOfRange(_, _) |
Error::SqliteSingleThreadedMode |
Error::InvalidParameterName(_) |
Error::ExecuteReturnedResults |
@@ -197,7 +207,7 @@ pub fn error_from_sqlite_code(code: c_int, message: Option<String>) -> Error {
Error::SqliteFailure(ffi::Error::new(code), message)
}
pub fn error_from_handle(db: *mut ffi::Struct_sqlite3, code: c_int) -> Error {
pub fn error_from_handle(db: *mut ffi::sqlite3, code: c_int) -> Error {
let message = if db.is_null() {
None
} else {

View File

@@ -54,7 +54,7 @@ use std::ffi::CStr;
use std::mem;
use std::ptr;
use std::slice;
use libc::{c_int, c_char, c_void};
use std::os::raw::{c_int, c_char, c_void};
use ffi;
use ffi::sqlite3_context;
@@ -79,7 +79,7 @@ pub fn set_result<'a>(ctx: *mut sqlite3_context, result: &ToSqlOutput<'a>) {
ValueRef::Null => unsafe { ffi::sqlite3_result_null(ctx) },
ValueRef::Integer(i) => unsafe { ffi::sqlite3_result_int64(ctx, i) },
ValueRef::Real(r) => unsafe { ffi::sqlite3_result_double(ctx, r) },
ValueRef::Text(ref s) => unsafe {
ValueRef::Text(s) => unsafe {
let length = s.len();
if length > ::std::i32::MAX as usize {
ffi::sqlite3_result_error_toobig(ctx);
@@ -97,7 +97,7 @@ pub fn set_result<'a>(ctx: *mut sqlite3_context, result: &ToSqlOutput<'a>) {
ffi::sqlite3_result_text(ctx, c_str.as_ptr(), length as c_int, destructor);
}
},
ValueRef::Blob(ref b) => unsafe {
ValueRef::Blob(b) => unsafe {
let length = b.len();
if length > ::std::i32::MAX as usize {
ffi::sqlite3_result_error_toobig(ctx);
@@ -114,8 +114,8 @@ pub fn set_result<'a>(ctx: *mut sqlite3_context, result: &ToSqlOutput<'a>) {
}
pub unsafe fn report_error(ctx: *mut sqlite3_context, err: &Error) {
match err {
&Error::SqliteFailure(ref err, ref s) => {
match *err {
Error::SqliteFailure(ref err, ref s) => {
ffi::sqlite3_result_error_code(ctx, err.extended_code);
if let Some(Ok(cstr)) = s.as_ref().map(|s| str_to_cstring(s)) {
ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
@@ -198,6 +198,7 @@ impl<'a> Context<'a> {
FromSqlError::InvalidType => {
Error::InvalidFunctionParameterType(idx, value.data_type())
}
FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx as c_int, i),
FromSqlError::Other(err) => {
Error::FromSqlConversionFailure(idx, value.data_type(), err)
}
@@ -507,7 +508,7 @@ mod test {
extern crate regex;
use std::collections::HashMap;
use libc::c_double;
use std::os::raw::c_double;
use self::regex::Regex;
use std::f64::EPSILON;

View File

@@ -52,7 +52,6 @@
//! ```
#![allow(unknown_lints)]
extern crate libc;
extern crate libsqlite3_sys as ffi;
extern crate lru_cache;
#[macro_use]
@@ -72,13 +71,18 @@ use std::cell::RefCell;
use std::ffi::{CStr, CString};
use std::result;
use std::str;
use libc::{c_int, c_char, c_void};
use std::sync::{Once, ONCE_INIT};
use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
use std::os::raw::{c_int, c_char};
use types::{ToSql, ToSqlOutput, FromSql, FromSqlError, ValueRef};
use types::{ToSql, FromSql, FromSqlError, ValueRef};
use error::{error_from_sqlite_code, error_from_handle};
use raw_statement::RawStatement;
use cache::StatementCache;
pub use statement::Statement;
use statement::StatementCrateImpl;
#[allow(deprecated)]
pub use transaction::{SqliteTransaction, SqliteTransactionBehavior};
pub use transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior};
@@ -86,20 +90,22 @@ pub use transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior}
#[allow(deprecated)]
pub use error::SqliteError;
pub use error::Error;
pub use ffi::ErrorCode;
pub use cache::CachedStatement;
pub use version::*;
#[cfg(feature = "load_extension")]
#[allow(deprecated)]
pub use load_extension_guard::{SqliteLoadExtensionGuard, LoadExtensionGuard};
pub mod types;
mod version;
mod transaction;
mod cache;
mod named_params;
mod error;
mod convenient;
mod raw_statement;
mod statement;
#[cfg(feature = "load_extension")]
mod load_extension_guard;
#[cfg(feature = "trace")]
@@ -110,6 +116,8 @@ pub mod backup;
pub mod functions;
#[cfg(feature = "blob")]
pub mod blob;
#[cfg(feature = "limits")]
pub mod limits;
#[cfg(all(feature = "vtab", feature = "functions"))]
pub mod vtab;
@@ -203,7 +211,7 @@ impl Connection {
/// Open a new connection to a SQLite database.
///
/// Database Connection](http://www.sqlite.org/c3ref/open.html) for a description of valid
/// [Database Connection](http://www.sqlite.org/c3ref/open.html) for a description of valid
/// flag combinations.
///
/// # Failure
@@ -223,7 +231,7 @@ impl Connection {
/// Open a new connection to an in-memory SQLite database.
///
/// Database Connection](http://www.sqlite.org/c3ref/open.html) for a description of valid
/// [Database Connection](http://www.sqlite.org/c3ref/open.html) for a description of valid
/// flag combinations.
///
/// # Failure
@@ -289,6 +297,28 @@ impl Connection {
self.prepare(sql).and_then(|mut stmt| stmt.execute(params))
}
/// Convenience method to prepare and execute a single SQL statement with named parameter(s).
///
/// On success, returns the number of rows that were changed or inserted or deleted (via
/// `sqlite3_changes`).
///
/// ## Example
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn insert(conn: &Connection) -> Result<i32> {
/// conn.execute_named("INSERT INTO test (name) VALUES (:name)", &[(":name", &"one")])
/// }
/// ```
///
/// # Failure
///
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
/// underlying SQLite call fails.
pub fn execute_named(&self, sql: &str, params: &[(&str, &ToSql)]) -> Result<c_int> {
self.prepare(sql).and_then(|mut stmt| stmt.execute_named(params))
}
/// Get the SQLite rowid of the most recent successful INSERT.
///
/// Uses [sqlite3_last_insert_rowid](https://www.sqlite.org/c3ref/last_insert_rowid.html) under
@@ -323,6 +353,24 @@ impl Connection {
stmt.query_row(params, f)
}
/// Convenience method to execute a query with named parameter(s) that is expected to return
/// a single row.
///
/// If the query returns more than one row, all rows except the first are ignored.
///
/// # Failure
///
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
/// underlying SQLite call fails.
pub fn query_row_named<T, F>(&self, sql: &str, params: &[(&str, &ToSql)], f: F) -> Result<T>
where F: FnOnce(&Row) -> T
{
let mut stmt = try!(self.prepare(sql));
let mut rows = try!(stmt.query_named(params));
rows.get_expected_row().map(|r| f(&r))
}
/// Convenience method to execute a query that is expected to return a single row,
/// and execute a mapping via `f` on that returned row with the possibility of failure.
/// The `Result` type of `f` must implement `std::convert::From<Error>`.
@@ -419,11 +467,8 @@ impl Connection {
/// Will return `Err` if the underlying SQLite call fails.
pub fn close(self) -> std::result::Result<(), (Connection, Error)> {
self.flush_prepared_statement_cache();
{
let mut db = self.db.borrow_mut();
db.close()
}
.map_err(move |err| (self, err))
let r = self.db.borrow_mut().close();
r.map_err(move |err| (self, err))
}
/// Enable loading of SQLite extensions. Strongly consider using `LoadExtensionGuard`
@@ -499,7 +544,7 @@ impl Connection {
/// on the rusqlite repository](https://github.com/jgallagher/rusqlite/issues) and describe
/// your use case. This function is unsafe because it gives you raw access to the SQLite
/// connection, and what you do with it could impact the safety of this `Connection`.
pub unsafe fn handle(&self) -> *mut ffi::Struct_sqlite3 {
pub unsafe fn handle(&self) -> *mut ffi::sqlite3 {
self.db.borrow().db()
}
@@ -521,7 +566,7 @@ impl fmt::Debug for Connection {
}
struct InnerConnection {
db: *mut ffi::Struct_sqlite3,
db: *mut ffi::sqlite3,
}
/// Old name for `OpenFlags`. `SqliteOpenFlags` is deprecated.
@@ -532,7 +577,7 @@ bitflags! {
#[doc = "Flags for opening SQLite database connections."]
#[doc = "See [sqlite3_open_v2](http://www.sqlite.org/c3ref/open.html) for details."]
#[repr(C)]
pub flags OpenFlags: ::libc::c_int {
pub flags OpenFlags: ::std::os::raw::c_int {
const SQLITE_OPEN_READ_ONLY = 0x00000001,
const SQLITE_OPEN_READ_WRITE = 0x00000002,
const SQLITE_OPEN_CREATE = 0x00000004,
@@ -551,29 +596,148 @@ impl Default for OpenFlags {
}
}
impl InnerConnection {
fn open_with_flags(c_path: &CString, flags: OpenFlags) -> Result<InnerConnection> {
unsafe {
// Before opening the database, we need to check that SQLite hasn't been
// compiled or configured to be in single-threaded mode. If it has, we're
// exposing a very unsafe API to Rust, so refuse to open connections at all.
// Unfortunately, the check for this is quite gross. sqlite3_threadsafe() only
// returns how SQLite was _compiled_; there is no public API to check whether
// someone called sqlite3_config() to set single-threaded mode. We can cheat
// by trying to allocate a mutex, though; in single-threaded mode due to
// compilation settings, the magic value 8 is returned (see the definition of
// sqlite3_mutex_alloc at https://github.com/mackyle/sqlite/blob/master/src/mutex.h);
// in single-threaded mode due to sqlite3_config(), the magic value 8 is also
// returned (see the definition of noopMutexAlloc at
// https://github.com/mackyle/sqlite/blob/master/src/mutex_noop.c).
const SQLITE_SINGLETHREADED_MUTEX_MAGIC: usize = 8;
static SQLITE_INIT: Once = ONCE_INIT;
static SQLITE_VERSION_CHECK: Once = ONCE_INIT;
static BYPASS_SQLITE_INIT: AtomicBool = ATOMIC_BOOL_INIT;
static BYPASS_VERSION_CHECK: AtomicBool = ATOMIC_BOOL_INIT;
/// rusqlite's check for a safe SQLite threading mode requires SQLite 3.7.0 or later. If you are
/// running against a SQLite older than that, rusqlite attempts to ensure safety by performing
/// configuration and initialization of SQLite itself the first time you attempt to open a
/// connection. By default, rusqlite panics if that initialization fails, since that could mean
/// SQLite has been initialized in single-thread mode.
///
/// If you are encountering that panic _and_ can ensure that SQLite has been initialized in either
/// multi-thread or serialized mode, call this function prior to attempting to open a connection
/// and rusqlite's initialization process will by skipped. This function is unsafe because if you
/// call it and SQLite has actually been configured to run in single-thread mode, you may enounter
/// memory errors or data corruption or any number of terrible things that should not be possible
/// when you're using Rust.
pub unsafe fn bypass_sqlite_initialization() {
BYPASS_SQLITE_INIT.store(true, Ordering::Relaxed);
}
/// rusqlite performs a one-time check that the runtime SQLite version is at least as new as
/// the version of SQLite found when rusqlite was built. Bypassing this check may be dangerous;
/// e.g., if you use features of SQLite that are not present in the runtime version. If you are
/// sure the runtime version is compatible with the build-time version for your usage, you can
/// bypass the version check by calling this function before your first connection attempt.
pub unsafe fn bypass_sqlite_version_check() {
BYPASS_VERSION_CHECK.store(true, Ordering::Relaxed);
}
fn ensure_valid_sqlite_version() {
SQLITE_VERSION_CHECK.call_once(|| {
let version_number = version_number();
// Check our hard floor.
if version_number < 3006008 {
panic!("rusqlite requires SQLite 3.6.8 or newer");
}
// Check that the major version number for runtime and buildtime match.
let buildtime_major = ffi::SQLITE_VERSION_NUMBER / 1_000_000;
let runtime_major = version_number / 1_000_000;
if buildtime_major != runtime_major {
panic!("rusqlite was built against SQLite {} but is running with SQLite {}",
str::from_utf8(ffi::SQLITE_VERSION).unwrap(),
version());
}
if BYPASS_VERSION_CHECK.load(Ordering::Relaxed) {
return;
}
// Check that the runtime version number is compatible with the version number we found at
// build-time.
if version_number < ffi::SQLITE_VERSION_NUMBER {
panic!("\
rusqlite was built against SQLite {} but the runtime SQLite version is {}. To fix this, either:
* Recompile rusqlite and link against the SQLite version you are using at runtime, or
* Call rusqlite::bypass_sqlite_version_check() prior to your first connection attempt. Doing this
means you're sure everything will work correctly even though the runtime version is older than
the version we found at build time.",
str::from_utf8(ffi::SQLITE_VERSION).unwrap(),
version());
}
});
}
fn ensure_safe_sqlite_threading_mode() -> Result<()> {
// Ensure SQLite was compiled in thredsafe mode.
if unsafe { ffi::sqlite3_threadsafe() == 0 } {
return Err(Error::SqliteSingleThreadedMode);
}
// Now we know SQLite is _capable_ of being in Multi-thread of Serialized mode, but it's
// possible someone configured it to be in Single-thread mode before calling into us. That
// would mean we're exposing an unsafe API via a safe one (in Rust terminology), which is
// no good. We have two options to protect against this, depending on the version of SQLite
// we're linked with:
//
// 1. If we're on 3.7.0 or later, we can ask SQLite for a mutex and check for the magic value
// 8. This isn't documented, but it's what SQLite returns for its mutex allocation function
// in Single-thread mode.
// 2. If we're prior to SQLite 3.7.0, AFAIK there's no way to check the threading mode. The
// check we perform for >= 3.7.0 will segfault. Instead, we insist on being able to call
// sqlite3_config and sqlite3_initialize ourself, ensuring we know the threading mode. This
// will fail if someone else has already initialized SQLite even if they initialized it
// safely. That's not ideal either, which is why we expose bypass_sqlite_initialization
// above.
if version_number() >= 3007000 {
const SQLITE_SINGLETHREADED_MUTEX_MAGIC: usize = 8;
let is_singlethreaded = unsafe {
let mutex_ptr = ffi::sqlite3_mutex_alloc(0);
let is_singlethreaded = mutex_ptr as usize == SQLITE_SINGLETHREADED_MUTEX_MAGIC;
ffi::sqlite3_mutex_free(mutex_ptr);
if is_singlethreaded {
return Err(Error::SqliteSingleThreadedMode);
is_singlethreaded
};
if is_singlethreaded {
Err(Error::SqliteSingleThreadedMode)
} else {
Ok(())
}
} else {
SQLITE_INIT.call_once(|| {
if BYPASS_SQLITE_INIT.load(Ordering::Relaxed) {
return;
}
unsafe {
let msg = "\
Could not ensure safe initialization of SQLite.
To fix this, either:
* Upgrade SQLite to at least version 3.7.0
* Ensure that SQLite has been initialized in Multi-thread or Serialized mode and call
rusqlite::bypass_sqlite_initialization() prior to your first connection attempt.";
if ffi::sqlite3_config(ffi::SQLITE_CONFIG_MULTITHREAD) != ffi::SQLITE_OK {
panic!(msg);
}
if ffi::sqlite3_initialize() != ffi::SQLITE_OK {
panic!(msg);
}
}
});
Ok(())
}
}
impl InnerConnection {
fn open_with_flags(c_path: &CString, flags: OpenFlags) -> Result<InnerConnection> {
ensure_valid_sqlite_version();
ensure_safe_sqlite_threading_mode()?;
// Replicate the check for sane open flags from SQLite, because the check in SQLite itself
// wasn't added until version 3.7.3.
debug_assert!(1 << SQLITE_OPEN_READ_ONLY.bits == 0x02);
debug_assert!(1 << SQLITE_OPEN_READ_WRITE.bits == 0x04);
debug_assert!(1 << (SQLITE_OPEN_READ_WRITE | SQLITE_OPEN_CREATE).bits == 0x40);
if (1 << (flags.bits & 0x7)) & 0x46 == 0 {
return Err(Error::SqliteFailure(ffi::Error::new(ffi::SQLITE_MISUSE), None));
}
unsafe {
let mut db: *mut ffi::sqlite3 = mem::uninitialized();
let r = ffi::sqlite3_open_v2(c_path.as_ptr(), &mut db, flags.bits(), ptr::null());
if r != ffi::SQLITE_OK {
@@ -601,7 +765,7 @@ impl InnerConnection {
}
}
fn db(&self) -> *mut ffi::Struct_sqlite3 {
fn db(&self) -> *mut ffi::sqlite3 {
self.db
}
@@ -660,7 +824,7 @@ impl InnerConnection {
Ok(())
} else {
let message = errmsg_to_string(&*errmsg);
ffi::sqlite3_free(errmsg as *mut libc::c_void);
ffi::sqlite3_free(errmsg as *mut ::std::os::raw::c_void);
Err(error_from_sqlite_code(r, Some(message)))
}
}
@@ -703,295 +867,6 @@ impl Drop for InnerConnection {
#[deprecated(since = "0.6.0", note = "Use Statement instead")]
pub type SqliteStatement<'conn> = Statement<'conn>;
/// A prepared statement.
pub struct Statement<'conn> {
conn: &'conn Connection,
stmt: RawStatement,
}
impl<'conn> Statement<'conn> {
fn new(conn: &Connection, stmt: RawStatement) -> Statement {
Statement {
conn: conn,
stmt: stmt,
}
}
/// Get all the column names in the result set of the prepared statement.
pub fn column_names(&self) -> Vec<&str> {
let n = self.column_count();
let mut cols = Vec::with_capacity(n as usize);
for i in 0..n {
let slice = self.stmt.column_name(i);
let s = str::from_utf8(slice.to_bytes()).unwrap();
cols.push(s);
}
cols
}
/// Return the number of columns in the result set returned by the prepared statement.
pub fn column_count(&self) -> i32 {
self.stmt.column_count()
}
/// Returns the column index in the result set for a given column name.
///
/// If there is no AS clause then the name of the column is unspecified and may change from one
/// release of SQLite to the next.
///
/// # Failure
///
/// Will return an `Error::InvalidColumnName` when there is no column with the specified `name`.
pub fn column_index(&self, name: &str) -> Result<i32> {
let bytes = name.as_bytes();
let n = self.column_count();
for i in 0..n {
if bytes == self.stmt.column_name(i).to_bytes() {
return Ok(i);
}
}
Err(Error::InvalidColumnName(String::from(name)))
}
/// Execute the prepared statement.
///
/// On success, returns the number of rows that were changed or inserted or deleted (via
/// `sqlite3_changes`).
///
/// ## Example
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn update_rows(conn: &Connection) -> Result<()> {
/// let mut stmt = try!(conn.prepare("UPDATE foo SET bar = 'baz' WHERE qux = ?"));
///
/// try!(stmt.execute(&[&1i32]));
/// try!(stmt.execute(&[&2i32]));
///
/// Ok(())
/// }
/// ```
///
/// # Failure
///
/// Will return `Err` if binding parameters fails, the executed statement returns rows (in
/// which case `query` should be used instead), or the underling SQLite call fails.
pub fn execute(&mut self, params: &[&ToSql]) -> Result<c_int> {
try!(self.bind_parameters(params));
self.execute_()
}
fn execute_(&mut self) -> Result<c_int> {
let r = self.stmt.step();
self.stmt.reset();
match r {
ffi::SQLITE_DONE => {
if self.column_count() == 0 {
Ok(self.conn.changes())
} else {
Err(Error::ExecuteReturnedResults)
}
}
ffi::SQLITE_ROW => Err(Error::ExecuteReturnedResults),
_ => Err(self.conn.decode_result(r).unwrap_err()),
}
}
/// Execute the prepared statement, returning a handle to the resulting rows.
///
/// Due to lifetime restricts, the rows handle returned by `query` does not
/// implement the `Iterator` trait. Consider using `query_map` or `query_and_then`
/// instead, which do.
///
/// ## Example
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn get_names(conn: &Connection) -> Result<Vec<String>> {
/// let mut stmt = try!(conn.prepare("SELECT name FROM people"));
/// let mut rows = try!(stmt.query(&[]));
///
/// let mut names = Vec::new();
/// while let Some(result_row) = rows.next() {
/// let row = try!(result_row);
/// names.push(row.get(0));
/// }
///
/// Ok(names)
/// }
/// ```
///
/// ## Failure
///
/// Will return `Err` if binding parameters fails.
pub fn query<'a>(&'a mut self, params: &[&ToSql]) -> Result<Rows<'a>> {
try!(self.bind_parameters(params));
Ok(Rows::new(self))
}
/// Executes the prepared statement and maps a function over the resulting rows, returning
/// an iterator over the mapped function results.
///
/// ## Example
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn get_names(conn: &Connection) -> Result<Vec<String>> {
/// let mut stmt = try!(conn.prepare("SELECT name FROM people"));
/// let rows = try!(stmt.query_map(&[], |row| row.get(0)));
///
/// let mut names = Vec::new();
/// for name_result in rows {
/// names.push(try!(name_result));
/// }
///
/// Ok(names)
/// }
/// ```
///
/// ## Failure
///
/// Will return `Err` if binding parameters fails.
pub fn query_map<'a, T, F>(&'a mut self, params: &[&ToSql], f: F) -> Result<MappedRows<'a, F>>
where F: FnMut(&Row) -> T
{
let row_iter = try!(self.query(params));
Ok(MappedRows {
rows: row_iter,
map: f,
})
}
/// Executes the prepared statement and maps a function over the resulting
/// rows, where the function returns a `Result` with `Error` type implementing
/// `std::convert::From<Error>` (so errors can be unified).
///
/// # Failure
///
/// Will return `Err` if binding parameters fails.
pub fn query_and_then<'a, T, E, F>(&'a mut self,
params: &[&ToSql],
f: F)
-> Result<AndThenRows<'a, F>>
where E: convert::From<Error>,
F: FnMut(&Row) -> result::Result<T, E>
{
let row_iter = try!(self.query(params));
Ok(AndThenRows {
rows: row_iter,
map: f,
})
}
/// Consumes the statement.
///
/// Functionally equivalent to the `Drop` implementation, but allows callers to see any errors
/// that occur.
///
/// # Failure
///
/// Will return `Err` if the underlying SQLite call fails.
pub fn finalize(mut self) -> Result<()> {
self.finalize_()
}
fn bind_parameter(&self, param: &ToSql, col: c_int) -> Result<()> {
let value = try!(param.to_sql());
let ptr = unsafe { self.stmt.ptr() };
let value = match value {
ToSqlOutput::Borrowed(v) => v,
ToSqlOutput::Owned(ref v) => ValueRef::from(v),
#[cfg(feature = "blob")]
ToSqlOutput::ZeroBlob(len) => {
return self.conn
.decode_result(unsafe { ffi::sqlite3_bind_zeroblob(ptr, col, len) });
}
};
self.conn.decode_result(match value {
ValueRef::Null => unsafe { ffi::sqlite3_bind_null(ptr, col) },
ValueRef::Integer(i) => unsafe { ffi::sqlite3_bind_int64(ptr, col, i) },
ValueRef::Real(r) => unsafe { ffi::sqlite3_bind_double(ptr, col, r) },
ValueRef::Text(ref s) => unsafe {
let length = s.len();
if length > ::std::i32::MAX as usize {
ffi::SQLITE_TOOBIG
} else {
let c_str = try!(str_to_cstring(s));
let destructor = if length > 0 {
ffi::SQLITE_TRANSIENT()
} else {
ffi::SQLITE_STATIC()
};
ffi::sqlite3_bind_text(ptr, col, c_str.as_ptr(), length as c_int, destructor)
}
},
ValueRef::Blob(ref b) => unsafe {
let length = b.len();
if length > ::std::i32::MAX as usize {
ffi::SQLITE_TOOBIG
} else if length == 0 {
ffi::sqlite3_bind_zeroblob(ptr, col, 0)
} else {
ffi::sqlite3_bind_blob(ptr,
col,
b.as_ptr() as *const c_void,
length as c_int,
ffi::SQLITE_TRANSIENT())
}
},
})
}
fn bind_parameters(&mut self, params: &[&ToSql]) -> Result<()> {
assert!(params.len() as c_int == self.stmt.bind_parameter_count(),
"incorrect number of parameters to query(): expected {}, got {}",
self.stmt.bind_parameter_count(),
params.len());
for (i, p) in params.iter().enumerate() {
try!(self.bind_parameter(*p, (i + 1) as c_int));
}
Ok(())
}
fn finalize_(&mut self) -> Result<()> {
let mut stmt = RawStatement::new(ptr::null_mut());
mem::swap(&mut stmt, &mut self.stmt);
self.conn.decode_result(stmt.finalize())
}
}
impl<'conn> Into<RawStatement> for Statement<'conn> {
fn into(mut self) -> RawStatement {
let mut stmt = RawStatement::new(ptr::null_mut());
mem::swap(&mut stmt, &mut self.stmt);
stmt
}
}
impl<'conn> fmt::Debug for Statement<'conn> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let sql = str::from_utf8(self.stmt.sql().to_bytes());
f.debug_struct("Statement")
.field("conn", self.conn)
.field("stmt", &self.stmt)
.field("sql", &sql)
.finish()
}
}
impl<'conn> Drop for Statement<'conn> {
#[allow(unused_must_use)]
fn drop(&mut self) {
self.finalize_();
}
}
/// An iterator over the mapped resulting rows of a query.
pub struct MappedRows<'stmt, F> {
rows: Rows<'stmt>,
@@ -1055,7 +930,7 @@ impl<'stmt> Rows<'stmt> {
fn reset(&mut self) {
if let Some(stmt) = self.stmt.take() {
stmt.stmt.reset();
stmt.reset();
}
}
@@ -1070,22 +945,18 @@ impl<'stmt> Rows<'stmt> {
/// "streaming iterator". For a more natural interface, consider using `query_map`
/// or `query_and_then` instead, which return types that implement `Iterator`.
pub fn next<'a>(&'a mut self) -> Option<Result<Row<'a, 'stmt>>> {
self.stmt.and_then(|stmt| {
match stmt.stmt.step() {
ffi::SQLITE_ROW => {
Some(Ok(Row {
stmt: stmt,
phantom: PhantomData,
}))
}
ffi::SQLITE_DONE => {
self.reset();
None
}
code => {
self.reset();
Some(Err(stmt.conn.decode_result(code).unwrap_err()))
}
self.stmt.and_then(|stmt| match stmt.step() {
Ok(true) => Some(Ok(Row {
stmt: stmt,
phantom: PhantomData,
})),
Ok(false) => {
self.reset();
None
}
Err(err) => {
self.reset();
Some(Err(err))
}
})
}
@@ -1112,9 +983,11 @@ impl<'a, 'stmt> Row<'a, 'stmt> {
///
/// ## Failure
///
/// Panics if the underlying SQLite column type is not a valid type as a source for `T`.
/// Panics if calling `row.get_checked(idx)` would return an error, including:
///
/// Panics if `idx` is outside the range of columns in the returned query.
/// * If the underlying SQLite column type is not a valid type as a source for `T`
/// * If the underlying SQLite integral value is outside the range representable by `T`
/// * If `idx` is outside the range of columns in the returned query
pub fn get<I: RowIndex, T: FromSql>(&self, idx: I) -> T {
self.get_checked(idx).unwrap()
}
@@ -1133,9 +1006,10 @@ impl<'a, 'stmt> Row<'a, 'stmt> {
/// for this row.
pub fn get_checked<I: RowIndex, T: FromSql>(&self, idx: I) -> Result<T> {
let idx = try!(idx.idx(self.stmt));
let value = unsafe { ValueRef::new(&self.stmt.stmt, idx) };
let value = self.stmt.value_ref(idx);
FromSql::column_result(value).map_err(|err| match err {
FromSqlError::InvalidType => Error::InvalidColumnType(idx, value.data_type()),
FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i),
FromSqlError::Other(err) => {
Error::FromSqlConversionFailure(idx as usize, value.data_type(), err)
}
@@ -1173,46 +1047,6 @@ impl<'a> RowIndex for &'a str {
}
}
impl<'a> ValueRef<'a> {
unsafe fn new(stmt: &RawStatement, col: c_int) -> ValueRef {
use std::slice::from_raw_parts;
let raw = stmt.ptr();
match stmt.column_type(col) {
ffi::SQLITE_NULL => ValueRef::Null,
ffi::SQLITE_INTEGER => ValueRef::Integer(ffi::sqlite3_column_int64(raw, col)),
ffi::SQLITE_FLOAT => ValueRef::Real(ffi::sqlite3_column_double(raw, col)),
ffi::SQLITE_TEXT => {
let text = ffi::sqlite3_column_text(raw, col);
assert!(!text.is_null(),
"unexpected SQLITE_TEXT column type with NULL data");
let s = CStr::from_ptr(text as *const c_char);
// sqlite3_column_text returns UTF8 data, so our unwrap here should be fine.
let s = s.to_str().expect("sqlite3_column_text returned invalid UTF-8");
ValueRef::Text(s)
}
ffi::SQLITE_BLOB => {
let blob = ffi::sqlite3_column_blob(raw, col);
let len = ffi::sqlite3_column_bytes(raw, col);
assert!(len >= 0,
"unexpected negative return from sqlite3_column_bytes");
if len > 0 {
assert!(!blob.is_null(),
"unexpected SQLITE_BLOB column type with NULL data");
ValueRef::Blob(from_raw_parts(blob as *const u8, len as usize))
} else {
// The return value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer.
ValueRef::Blob(&[])
}
}
_ => unreachable!("sqlite3_column_type returned invalid value"),
}
}
}
#[cfg(test)]
mod test {
extern crate tempdir;
@@ -1273,7 +1107,7 @@ mod test {
let raw_stmt = {
use std::mem;
use std::ptr;
use libc::c_int;
use std::os::raw::c_int;
use super::str_to_cstring;
let raw_db = db.db.borrow_mut().db;
@@ -1339,7 +1173,7 @@ mod test {
db.execute("INSERT INTO foo(x) VALUES (?)", &[&2i32]).unwrap());
assert_eq!(3i32,
db.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap());
db.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap());
}
#[test]
@@ -1455,7 +1289,7 @@ mod test {
db.execute_batch(sql).unwrap();
assert_eq!(10i64,
db.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
db.query_row::<i64, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
.unwrap());
let result: Result<i64> = db.query_row("SELECT x FROM foo WHERE x > 5", &[], |r| r.get(0));
@@ -1512,12 +1346,11 @@ mod test {
match result.unwrap_err() {
Error::SqliteFailure(err, _) => {
assert_eq!(err.code, ffi::ErrorCode::ConstraintViolation);
assert_eq!(err.code, ErrorCode::ConstraintViolation);
// extended error codes for constraints were added in SQLite 3.7.16; if we're
// running on a version at least that new, check for the extended code
let version = unsafe { ffi::sqlite3_libversion_number() };
if version >= 3007016 {
if version_number() >= 3007016 {
assert_eq!(err.extended_code, ffi::SQLITE_CONSTRAINT_NOTNULL)
}
}
@@ -1525,6 +1358,16 @@ mod test {
}
}
#[test]
fn test_version_string() {
let n = version_number();
let major = n / 1_000_000;
let minor = (n % 1_000_000) / 1_000;
let patch = n % 1_000;
assert!(version().contains(&format!("{}.{}.{}", major, minor, patch)));
}
mod query_and_then_tests {
extern crate libsqlite3_sys as ffi;
use super::*;

71
src/limits.rs Normal file
View File

@@ -0,0 +1,71 @@
//! Run-Time Limits
use std::os::raw::c_int;
use ffi;
pub use ffi::Limit;
use Connection;
impl Connection {
/// Returns the current value of a limit.
pub fn limit(&self, limit: Limit) -> i32 {
let c = self.db.borrow();
unsafe { ffi::sqlite3_limit(c.db(), limit as c_int, -1) }
}
/// Changes the limit to `new_val`, returning the prior value of the limit.
pub fn set_limit(&self, limit: Limit, new_val: i32) -> i32 {
let c = self.db.borrow_mut();
unsafe { ffi::sqlite3_limit(c.db(), limit as c_int, new_val) }
}
}
#[cfg(test)]
mod test {
use ffi::Limit;
use Connection;
#[test]
fn test_limit() {
let db = Connection::open_in_memory().unwrap();
db.set_limit(Limit::SQLITE_LIMIT_LENGTH, 1024);
assert_eq!(1024, db.limit(Limit::SQLITE_LIMIT_LENGTH));
db.set_limit(Limit::SQLITE_LIMIT_SQL_LENGTH, 1024);
assert_eq!(1024, db.limit(Limit::SQLITE_LIMIT_SQL_LENGTH));
db.set_limit(Limit::SQLITE_LIMIT_COLUMN, 64);
assert_eq!(64, db.limit(Limit::SQLITE_LIMIT_COLUMN));
db.set_limit(Limit::SQLITE_LIMIT_EXPR_DEPTH, 256);
assert_eq!(256, db.limit(Limit::SQLITE_LIMIT_EXPR_DEPTH));
db.set_limit(Limit::SQLITE_LIMIT_COMPOUND_SELECT, 32);
assert_eq!(32, db.limit(Limit::SQLITE_LIMIT_COMPOUND_SELECT));
db.set_limit(Limit::SQLITE_LIMIT_FUNCTION_ARG, 32);
assert_eq!(32, db.limit(Limit::SQLITE_LIMIT_FUNCTION_ARG));
db.set_limit(Limit::SQLITE_LIMIT_ATTACHED, 2);
assert_eq!(2, db.limit(Limit::SQLITE_LIMIT_ATTACHED));
db.set_limit(Limit::SQLITE_LIMIT_LIKE_PATTERN_LENGTH, 128);
assert_eq!(128, db.limit(Limit::SQLITE_LIMIT_LIKE_PATTERN_LENGTH));
db.set_limit(Limit::SQLITE_LIMIT_VARIABLE_NUMBER, 99);
assert_eq!(99, db.limit(Limit::SQLITE_LIMIT_VARIABLE_NUMBER));
// SQLITE_LIMIT_TRIGGER_DEPTH was added in SQLite 3.6.18.
if ::version_number() >= 3006018 {
db.set_limit(Limit::SQLITE_LIMIT_TRIGGER_DEPTH, 32);
assert_eq!(32, db.limit(Limit::SQLITE_LIMIT_TRIGGER_DEPTH));
}
// SQLITE_LIMIT_WORKER_THREADS was added in SQLite 3.8.7.
if ::version_number() >= 3008007 {
db.set_limit(Limit::SQLITE_LIMIT_WORKER_THREADS, 2);
assert_eq!(2, db.limit(Limit::SQLITE_LIMIT_WORKER_THREADS));
}
}
}

View File

@@ -1,356 +0,0 @@
use std::convert;
use std::result;
use libc::c_int;
use {Result, Error, Connection, Statement, MappedRows, AndThenRows, Rows, Row, str_to_cstring};
use types::ToSql;
impl Connection {
/// Convenience method to prepare and execute a single SQL statement with named parameter(s).
///
/// On success, returns the number of rows that were changed or inserted or deleted (via
/// `sqlite3_changes`).
///
/// ## Example
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn insert(conn: &Connection) -> Result<i32> {
/// conn.execute_named("INSERT INTO test (name) VALUES (:name)", &[(":name", &"one")])
/// }
/// ```
///
/// # Failure
///
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
/// underlying SQLite call fails.
pub fn execute_named(&self, sql: &str, params: &[(&str, &ToSql)]) -> Result<c_int> {
self.prepare(sql).and_then(|mut stmt| stmt.execute_named(params))
}
/// Convenience method to execute a query with named parameter(s) that is expected to return
/// a single row.
///
/// If the query returns more than one row, all rows except the first are ignored.
///
/// # Failure
///
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
/// underlying SQLite call fails.
pub fn query_row_named<T, F>(&self, sql: &str, params: &[(&str, &ToSql)], f: F) -> Result<T>
where F: FnOnce(&Row) -> T
{
let mut stmt = try!(self.prepare(sql));
let mut rows = try!(stmt.query_named(params));
rows.get_expected_row().map(|r| f(&r))
}
}
impl<'conn> Statement<'conn> {
/// Return the index of an SQL parameter given its name.
///
/// # Failure
///
/// Will return Err if `name` is invalid. Will return Ok(None) if the name
/// is valid but not a bound parameter of this statement.
pub fn parameter_index(&self, name: &str) -> Result<Option<i32>> {
let c_name = try!(str_to_cstring(name));
Ok(self.stmt.bind_parameter_index(&c_name))
}
/// Execute the prepared statement with named parameter(s). If any parameters
/// that were in the prepared statement are not included in `params`, they
/// will continue to use the most-recently bound value from a previous call
/// to `execute_named`, or `NULL` if they have never been bound.
///
/// On success, returns the number of rows that were changed or inserted or deleted (via
/// `sqlite3_changes`).
///
/// ## Example
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn insert(conn: &Connection) -> Result<i32> {
/// let mut stmt = try!(conn.prepare("INSERT INTO test (name) VALUES (:name)"));
/// stmt.execute_named(&[(":name", &"one")])
/// }
/// ```
///
/// # Failure
///
/// Will return `Err` if binding parameters fails, the executed statement returns rows (in
/// which case `query` should be used instead), or the underling SQLite call fails.
pub fn execute_named(&mut self, params: &[(&str, &ToSql)]) -> Result<c_int> {
try!(self.bind_parameters_named(params));
self.execute_()
}
/// Execute the prepared statement with named parameter(s), returning a handle for the
/// resulting rows. If any parameters that were in the prepared statement are not included in
/// `params`, they will continue to use the most-recently bound value from a previous call to
/// `query_named`, or `NULL` if they have never been bound.
///
/// ## Example
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn query(conn: &Connection) -> Result<()> {
/// let mut stmt = try!(conn.prepare("SELECT * FROM test where name = :name"));
/// let mut rows = try!(stmt.query_named(&[(":name", &"one")]));
/// while let Some(row) = rows.next() {
/// // ...
/// }
/// Ok(())
/// }
/// ```
///
/// # Failure
///
/// Will return `Err` if binding parameters fails.
pub fn query_named<'a>(&'a mut self, params: &[(&str, &ToSql)]) -> Result<Rows<'a>> {
try!(self.bind_parameters_named(params));
Ok(Rows::new(self))
}
/// Execute the prepared statement with named parameter(s), returning an iterator over the
/// result of calling the mapping function over the query's rows. If any parameters that were
/// in the prepared statement are not included in `params`, they will continue to use the
/// most-recently bound value from a previous call to `query_named`, or `NULL` if they have
/// never been bound.
///
/// ## Example
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn get_names(conn: &Connection) -> Result<Vec<String>> {
/// let mut stmt = try!(conn.prepare("SELECT name FROM people WHERE id = :id"));
/// let rows = try!(stmt.query_map_named(&[(":id", &"one")], |row| row.get(0)));
///
/// let mut names = Vec::new();
/// for name_result in rows {
/// names.push(try!(name_result));
/// }
///
/// Ok(names)
/// }
/// ```
///
/// ## Failure
///
/// Will return `Err` if binding parameters fails.
pub fn query_map_named<'a, T, F>(&'a mut self,
params: &[(&str, &ToSql)],
f: F)
-> Result<MappedRows<'a, F>>
where F: FnMut(&Row) -> T
{
let rows = try!(self.query_named(params));
Ok(MappedRows {
rows: rows,
map: f,
})
}
/// Execute the prepared statement with named parameter(s), returning an iterator over the
/// result of calling the mapping function over the query's rows. If any parameters that were
/// in the prepared statement are not included in `params`, they will continue to use the
/// most-recently bound value from a previous call to `query_named`, or `NULL` if they have
/// never been bound.
///
/// ## Example
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// struct Person { name: String };
///
/// fn name_to_person(name: String) -> Result<Person> {
/// // ... check for valid name
/// Ok(Person{ name: name })
/// }
///
/// fn get_names(conn: &Connection) -> Result<Vec<Person>> {
/// let mut stmt = try!(conn.prepare("SELECT name FROM people WHERE id = :id"));
/// let rows = try!(stmt.query_and_then_named(&[(":id", &"one")], |row| {
/// name_to_person(row.get(0))
/// }));
///
/// let mut persons = Vec::new();
/// for person_result in rows {
/// persons.push(try!(person_result));
/// }
///
/// Ok(persons)
/// }
/// ```
///
/// ## Failure
///
/// Will return `Err` if binding parameters fails.
pub fn query_and_then_named<'a, T, E, F>(&'a mut self,
params: &[(&str, &ToSql)],
f: F)
-> Result<AndThenRows<'a, F>>
where E: convert::From<Error>,
F: FnMut(&Row) -> result::Result<T, E>
{
let rows = try!(self.query_named(params));
Ok(AndThenRows {
rows: rows,
map: f,
})
}
fn bind_parameters_named(&mut self, params: &[(&str, &ToSql)]) -> Result<()> {
for &(name, value) in params {
if let Some(i) = try!(self.parameter_index(name)) {
try!(self.bind_parameter(value, i));
} else {
return Err(Error::InvalidParameterName(name.into()));
}
}
Ok(())
}
}
#[cfg(test)]
mod test {
use Connection;
use error::Error;
#[test]
fn test_execute_named() {
let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap();
assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)]).unwrap(),
1);
assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)]).unwrap(),
1);
assert_eq!(3i32,
db.query_row_named("SELECT SUM(x) FROM foo WHERE x > :x",
&[(":x", &0i32)],
|r| r.get(0))
.unwrap());
}
#[test]
fn test_stmt_execute_named() {
let db = Connection::open_in_memory().unwrap();
let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \
INTEGER)";
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("INSERT INTO test (name) VALUES (:name)").unwrap();
stmt.execute_named(&[(":name", &"one")]).unwrap();
assert_eq!(1i32,
db.query_row_named("SELECT COUNT(*) FROM test WHERE name = :name",
&[(":name", &"one")],
|r| r.get(0))
.unwrap());
}
#[test]
fn test_query_named() {
let db = Connection::open_in_memory().unwrap();
let sql = r#"
CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER);
INSERT INTO test(id, name) VALUES (1, "one");
"#;
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT id FROM test where name = :name").unwrap();
let mut rows = stmt.query_named(&[(":name", &"one")]).unwrap();
let id: i32 = rows.next().unwrap().unwrap().get(0);
assert_eq!(1, id);
}
#[test]
fn test_query_map_named() {
let db = Connection::open_in_memory().unwrap();
let sql = r#"
CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER);
INSERT INTO test(id, name) VALUES (1, "one");
"#;
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT id FROM test where name = :name").unwrap();
let mut rows = stmt.query_map_named(&[(":name", &"one")], |row| {
let id: i32 = row.get(0);
2 * id
})
.unwrap();
let doubled_id: i32 = rows.next().unwrap().unwrap();
assert_eq!(2, doubled_id);
}
#[test]
fn test_query_and_then_named() {
let db = Connection::open_in_memory().unwrap();
let sql = r#"
CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER);
INSERT INTO test(id, name) VALUES (1, "one");
INSERT INTO test(id, name) VALUES (2, "one");
"#;
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT id FROM test where name = :name ORDER BY id ASC")
.unwrap();
let mut rows = stmt.query_and_then_named(&[(":name", &"one")], |row| {
let id: i32 = row.get(0);
if id == 1 {
Ok(id)
} else {
Err(Error::SqliteSingleThreadedMode)
}
})
.unwrap();
// first row should be Ok
let doubled_id: i32 = rows.next().unwrap().unwrap();
assert_eq!(1, doubled_id);
// second row should be Err
match rows.next().unwrap() {
Ok(_) => panic!("invalid Ok"),
Err(Error::SqliteSingleThreadedMode) => (),
Err(_) => panic!("invalid Err"),
}
}
#[test]
fn test_unbound_parameters_are_null() {
let db = Connection::open_in_memory().unwrap();
let sql = "CREATE TABLE test (x TEXT, y TEXT)";
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)").unwrap();
stmt.execute_named(&[(":x", &"one")]).unwrap();
let result: Option<String> =
db.query_row("SELECT y FROM test WHERE x = 'one'", &[], |row| row.get(0))
.unwrap();
assert!(result.is_none());
}
#[test]
fn test_unbound_parameters_are_reused() {
let db = Connection::open_in_memory().unwrap();
let sql = "CREATE TABLE test (x TEXT, y TEXT)";
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)").unwrap();
stmt.execute_named(&[(":x", &"one")]).unwrap();
stmt.execute_named(&[(":y", &"two")]).unwrap();
let result: String =
db.query_row("SELECT x FROM test WHERE y = 'two'", &[], |row| row.get(0))
.unwrap();
assert_eq!(result, "one");
}
}

View File

@@ -1,6 +1,6 @@
use std::ffi::CStr;
use std::ptr;
use libc::c_int;
use std::os::raw::c_int;
use super::ffi;
// Private newtype for raw sqlite3_stmts that finalize themselves when dropped.

779
src/statement.rs Normal file
View File

@@ -0,0 +1,779 @@
use std::{convert, fmt, mem, ptr, result, str};
use std::ffi::CStr;
use std::os::raw::{c_char, c_int, c_void};
use std::slice::from_raw_parts;
use super::ffi;
use super::{Connection, RawStatement, Result, Error, ValueRef, Row, Rows, AndThenRows, MappedRows};
use super::str_to_cstring;
use types::{ToSql, ToSqlOutput};
/// A prepared statement.
pub struct Statement<'conn> {
conn: &'conn Connection,
stmt: RawStatement,
}
impl<'conn> Statement<'conn> {
/// Get all the column names in the result set of the prepared statement.
pub fn column_names(&self) -> Vec<&str> {
let n = self.column_count();
let mut cols = Vec::with_capacity(n as usize);
for i in 0..n {
let slice = self.stmt.column_name(i);
let s = str::from_utf8(slice.to_bytes()).unwrap();
cols.push(s);
}
cols
}
/// Return the number of columns in the result set returned by the prepared statement.
pub fn column_count(&self) -> i32 {
self.stmt.column_count()
}
/// Returns the column index in the result set for a given column name.
///
/// If there is no AS clause then the name of the column is unspecified and may change from one
/// release of SQLite to the next.
///
/// # Failure
///
/// Will return an `Error::InvalidColumnName` when there is no column with the specified `name`.
pub fn column_index(&self, name: &str) -> Result<i32> {
let bytes = name.as_bytes();
let n = self.column_count();
for i in 0..n {
if bytes == self.stmt.column_name(i).to_bytes() {
return Ok(i);
}
}
Err(Error::InvalidColumnName(String::from(name)))
}
/// Execute the prepared statement.
///
/// On success, returns the number of rows that were changed or inserted or deleted (via
/// `sqlite3_changes`).
///
/// ## Example
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn update_rows(conn: &Connection) -> Result<()> {
/// let mut stmt = try!(conn.prepare("UPDATE foo SET bar = 'baz' WHERE qux = ?"));
///
/// try!(stmt.execute(&[&1i32]));
/// try!(stmt.execute(&[&2i32]));
///
/// Ok(())
/// }
/// ```
///
/// # Failure
///
/// Will return `Err` if binding parameters fails, the executed statement returns rows (in
/// which case `query` should be used instead), or the underling SQLite call fails.
pub fn execute(&mut self, params: &[&ToSql]) -> Result<c_int> {
try!(self.bind_parameters(params));
self.execute_with_bound_parameters()
}
/// Execute the prepared statement with named parameter(s). If any parameters
/// that were in the prepared statement are not included in `params`, they
/// will continue to use the most-recently bound value from a previous call
/// to `execute_named`, or `NULL` if they have never been bound.
///
/// On success, returns the number of rows that were changed or inserted or deleted (via
/// `sqlite3_changes`).
///
/// ## Example
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn insert(conn: &Connection) -> Result<i32> {
/// let mut stmt = try!(conn.prepare("INSERT INTO test (name) VALUES (:name)"));
/// stmt.execute_named(&[(":name", &"one")])
/// }
/// ```
///
/// # Failure
///
/// Will return `Err` if binding parameters fails, the executed statement returns rows (in
/// which case `query` should be used instead), or the underling SQLite call fails.
pub fn execute_named(&mut self, params: &[(&str, &ToSql)]) -> Result<c_int> {
try!(self.bind_parameters_named(params));
self.execute_with_bound_parameters()
}
/// Execute an INSERT and return the ROWID.
///
/// # Note
///
/// This function is a convenience wrapper around `execute()` intended for queries that
/// insert a single item. It is possible to misuse this function in a way that it cannot
/// detect, such as by calling it on a statement which _updates_ a single item rather than
/// inserting one. Please don't do that.
///
/// # Failure
///
/// Will return `Err` if no row is inserted or many rows are inserted.
pub fn insert(&mut self, params: &[&ToSql]) -> Result<i64> {
let changes = try!(self.execute(params));
match changes {
1 => Ok(self.conn.last_insert_rowid()),
_ => Err(Error::StatementChangedRows(changes)),
}
}
/// Execute the prepared statement, returning a handle to the resulting rows.
///
/// Due to lifetime restricts, the rows handle returned by `query` does not
/// implement the `Iterator` trait. Consider using `query_map` or `query_and_then`
/// instead, which do.
///
/// ## Example
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn get_names(conn: &Connection) -> Result<Vec<String>> {
/// let mut stmt = try!(conn.prepare("SELECT name FROM people"));
/// let mut rows = try!(stmt.query(&[]));
///
/// let mut names = Vec::new();
/// while let Some(result_row) = rows.next() {
/// let row = try!(result_row);
/// names.push(row.get(0));
/// }
///
/// Ok(names)
/// }
/// ```
///
/// ## Failure
///
/// Will return `Err` if binding parameters fails.
pub fn query<'a>(&'a mut self, params: &[&ToSql]) -> Result<Rows<'a>> {
try!(self.bind_parameters(params));
Ok(Rows::new(self))
}
/// Execute the prepared statement with named parameter(s), returning a handle for the
/// resulting rows. If any parameters that were in the prepared statement are not included in
/// `params`, they will continue to use the most-recently bound value from a previous call to
/// `query_named`, or `NULL` if they have never been bound.
///
/// ## Example
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn query(conn: &Connection) -> Result<()> {
/// let mut stmt = try!(conn.prepare("SELECT * FROM test where name = :name"));
/// let mut rows = try!(stmt.query_named(&[(":name", &"one")]));
/// while let Some(row) = rows.next() {
/// // ...
/// }
/// Ok(())
/// }
/// ```
///
/// # Failure
///
/// Will return `Err` if binding parameters fails.
pub fn query_named<'a>(&'a mut self, params: &[(&str, &ToSql)]) -> Result<Rows<'a>> {
try!(self.bind_parameters_named(params));
Ok(Rows::new(self))
}
/// Executes the prepared statement and maps a function over the resulting rows, returning
/// an iterator over the mapped function results.
///
/// ## Example
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn get_names(conn: &Connection) -> Result<Vec<String>> {
/// let mut stmt = try!(conn.prepare("SELECT name FROM people"));
/// let rows = try!(stmt.query_map(&[], |row| row.get(0)));
///
/// let mut names = Vec::new();
/// for name_result in rows {
/// names.push(try!(name_result));
/// }
///
/// Ok(names)
/// }
/// ```
///
/// ## Failure
///
/// Will return `Err` if binding parameters fails.
pub fn query_map<'a, T, F>(&'a mut self, params: &[&ToSql], f: F) -> Result<MappedRows<'a, F>>
where F: FnMut(&Row) -> T
{
let row_iter = try!(self.query(params));
Ok(MappedRows {
rows: row_iter,
map: f,
})
}
/// Execute the prepared statement with named parameter(s), returning an iterator over the
/// result of calling the mapping function over the query's rows. If any parameters that were
/// in the prepared statement are not included in `params`, they will continue to use the
/// most-recently bound value from a previous call to `query_named`, or `NULL` if they have
/// never been bound.
///
/// ## Example
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn get_names(conn: &Connection) -> Result<Vec<String>> {
/// let mut stmt = try!(conn.prepare("SELECT name FROM people WHERE id = :id"));
/// let rows = try!(stmt.query_map_named(&[(":id", &"one")], |row| row.get(0)));
///
/// let mut names = Vec::new();
/// for name_result in rows {
/// names.push(try!(name_result));
/// }
///
/// Ok(names)
/// }
/// ```
///
/// ## Failure
///
/// Will return `Err` if binding parameters fails.
pub fn query_map_named<'a, T, F>(&'a mut self,
params: &[(&str, &ToSql)],
f: F)
-> Result<MappedRows<'a, F>>
where F: FnMut(&Row) -> T
{
let rows = try!(self.query_named(params));
Ok(MappedRows {
rows: rows,
map: f,
})
}
/// Executes the prepared statement and maps a function over the resulting
/// rows, where the function returns a `Result` with `Error` type implementing
/// `std::convert::From<Error>` (so errors can be unified).
///
/// # Failure
///
/// Will return `Err` if binding parameters fails.
pub fn query_and_then<'a, T, E, F>(&'a mut self,
params: &[&ToSql],
f: F)
-> Result<AndThenRows<'a, F>>
where E: convert::From<Error>,
F: FnMut(&Row) -> result::Result<T, E>
{
let row_iter = try!(self.query(params));
Ok(AndThenRows {
rows: row_iter,
map: f,
})
}
/// Execute the prepared statement with named parameter(s), returning an iterator over the
/// result of calling the mapping function over the query's rows. If any parameters that were
/// in the prepared statement are not included in `params`, they will continue to use the
/// most-recently bound value from a previous call to `query_named`, or `NULL` if they have
/// never been bound.
///
/// ## Example
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// struct Person { name: String };
///
/// fn name_to_person(name: String) -> Result<Person> {
/// // ... check for valid name
/// Ok(Person{ name: name })
/// }
///
/// fn get_names(conn: &Connection) -> Result<Vec<Person>> {
/// let mut stmt = try!(conn.prepare("SELECT name FROM people WHERE id = :id"));
/// let rows = try!(stmt.query_and_then_named(&[(":id", &"one")], |row| {
/// name_to_person(row.get(0))
/// }));
///
/// let mut persons = Vec::new();
/// for person_result in rows {
/// persons.push(try!(person_result));
/// }
///
/// Ok(persons)
/// }
/// ```
///
/// ## Failure
///
/// Will return `Err` if binding parameters fails.
pub fn query_and_then_named<'a, T, E, F>(&'a mut self,
params: &[(&str, &ToSql)],
f: F)
-> Result<AndThenRows<'a, F>>
where E: convert::From<Error>,
F: FnMut(&Row) -> result::Result<T, E>
{
let rows = try!(self.query_named(params));
Ok(AndThenRows {
rows: rows,
map: f,
})
}
/// Return `true` if a query in the SQL statement it executes returns one or more rows
/// and `false` if the SQL returns an empty set.
pub fn exists(&mut self, params: &[&ToSql]) -> Result<bool> {
let mut rows = try!(self.query(params));
let exists = {
match rows.next() {
Some(_) => true,
None => false,
}
};
Ok(exists)
}
/// Convenience method to execute a query that is expected to return a single row.
///
/// If the query returns more than one row, all rows except the first are ignored.
///
/// # Failure
///
/// Will return `Err` if the underlying SQLite call fails.
pub fn query_row<T, F>(&mut self, params: &[&ToSql], f: F) -> Result<T>
where F: FnOnce(&Row) -> T
{
let mut rows = try!(self.query(params));
rows.get_expected_row().map(|r| f(&r))
}
/// Consumes the statement.
///
/// Functionally equivalent to the `Drop` implementation, but allows callers to see any errors
/// that occur.
///
/// # Failure
///
/// Will return `Err` if the underlying SQLite call fails.
pub fn finalize(mut self) -> Result<()> {
self.finalize_()
}
/// Return the index of an SQL parameter given its name.
///
/// # Failure
///
/// Will return Err if `name` is invalid. Will return Ok(None) if the name
/// is valid but not a bound parameter of this statement.
pub fn parameter_index(&self, name: &str) -> Result<Option<i32>> {
let c_name = try!(str_to_cstring(name));
Ok(self.stmt.bind_parameter_index(&c_name))
}
fn bind_parameters(&mut self, params: &[&ToSql]) -> Result<()> {
assert!(params.len() as c_int == self.stmt.bind_parameter_count(),
"incorrect number of parameters to query(): expected {}, got {}",
self.stmt.bind_parameter_count(),
params.len());
for (i, p) in params.iter().enumerate() {
try!(self.bind_parameter(*p, (i + 1) as c_int));
}
Ok(())
}
fn bind_parameters_named(&mut self, params: &[(&str, &ToSql)]) -> Result<()> {
for &(name, value) in params {
if let Some(i) = try!(self.parameter_index(name)) {
try!(self.bind_parameter(value, i));
} else {
return Err(Error::InvalidParameterName(name.into()));
}
}
Ok(())
}
fn bind_parameter(&self, param: &ToSql, col: c_int) -> Result<()> {
let value = try!(param.to_sql());
let ptr = unsafe { self.stmt.ptr() };
let value = match value {
ToSqlOutput::Borrowed(v) => v,
ToSqlOutput::Owned(ref v) => ValueRef::from(v),
#[cfg(feature = "blob")]
ToSqlOutput::ZeroBlob(len) => {
return self.conn
.decode_result(unsafe { ffi::sqlite3_bind_zeroblob(ptr, col, len) });
}
};
self.conn.decode_result(match value {
ValueRef::Null => unsafe { ffi::sqlite3_bind_null(ptr, col) },
ValueRef::Integer(i) => unsafe { ffi::sqlite3_bind_int64(ptr, col, i) },
ValueRef::Real(r) => unsafe { ffi::sqlite3_bind_double(ptr, col, r) },
ValueRef::Text(s) => unsafe {
let length = s.len();
if length > ::std::i32::MAX as usize {
ffi::SQLITE_TOOBIG
} else {
let c_str = try!(str_to_cstring(s));
let destructor = if length > 0 {
ffi::SQLITE_TRANSIENT()
} else {
ffi::SQLITE_STATIC()
};
ffi::sqlite3_bind_text(ptr, col, c_str.as_ptr(), length as c_int, destructor)
}
},
ValueRef::Blob(b) => unsafe {
let length = b.len();
if length > ::std::i32::MAX as usize {
ffi::SQLITE_TOOBIG
} else if length == 0 {
ffi::sqlite3_bind_zeroblob(ptr, col, 0)
} else {
ffi::sqlite3_bind_blob(ptr,
col,
b.as_ptr() as *const c_void,
length as c_int,
ffi::SQLITE_TRANSIENT())
}
},
})
}
fn execute_with_bound_parameters(&mut self) -> Result<c_int> {
let r = self.stmt.step();
self.stmt.reset();
match r {
ffi::SQLITE_DONE => {
if self.column_count() == 0 {
Ok(self.conn.changes())
} else {
Err(Error::ExecuteReturnedResults)
}
}
ffi::SQLITE_ROW => Err(Error::ExecuteReturnedResults),
_ => Err(self.conn.decode_result(r).unwrap_err()),
}
}
fn finalize_(&mut self) -> Result<()> {
let mut stmt = RawStatement::new(ptr::null_mut());
mem::swap(&mut stmt, &mut self.stmt);
self.conn.decode_result(stmt.finalize())
}
}
impl<'conn> Into<RawStatement> for Statement<'conn> {
fn into(mut self) -> RawStatement {
let mut stmt = RawStatement::new(ptr::null_mut());
mem::swap(&mut stmt, &mut self.stmt);
stmt
}
}
impl<'conn> fmt::Debug for Statement<'conn> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let sql = str::from_utf8(self.stmt.sql().to_bytes());
f.debug_struct("Statement")
.field("conn", self.conn)
.field("stmt", &self.stmt)
.field("sql", &sql)
.finish()
}
}
impl<'conn> Drop for Statement<'conn> {
#[allow(unused_must_use)]
fn drop(&mut self) {
self.finalize_();
}
}
// TODO: This trait lets us have "pub(crate)" visibility on some Statement methods. Remove this
// once pub(crate) is stable.
pub trait StatementCrateImpl<'conn> {
fn new(conn: &'conn Connection, stmt: RawStatement) -> Self;
fn value_ref(&self, col: c_int) -> ValueRef;
fn step(&self) -> Result<bool>;
fn reset(&self) -> c_int;
}
impl<'conn> StatementCrateImpl<'conn> for Statement<'conn> {
fn new(conn: &Connection, stmt: RawStatement) -> Statement {
Statement {
conn: conn,
stmt: stmt,
}
}
fn value_ref(&self, col: c_int) -> ValueRef {
let raw = unsafe { self.stmt.ptr() };
match self.stmt.column_type(col) {
ffi::SQLITE_NULL => ValueRef::Null,
ffi::SQLITE_INTEGER => {
ValueRef::Integer(unsafe { ffi::sqlite3_column_int64(raw, col) })
}
ffi::SQLITE_FLOAT => ValueRef::Real(unsafe { ffi::sqlite3_column_double(raw, col) }),
ffi::SQLITE_TEXT => {
let s = unsafe {
let text = ffi::sqlite3_column_text(raw, col);
assert!(!text.is_null(),
"unexpected SQLITE_TEXT column type with NULL data");
CStr::from_ptr(text as *const c_char)
};
// sqlite3_column_text returns UTF8 data, so our unwrap here should be fine.
let s = s.to_str().expect("sqlite3_column_text returned invalid UTF-8");
ValueRef::Text(s)
}
ffi::SQLITE_BLOB => {
let (blob, len) = unsafe {
(ffi::sqlite3_column_blob(raw, col), ffi::sqlite3_column_bytes(raw, col))
};
assert!(len >= 0,
"unexpected negative return from sqlite3_column_bytes");
if len > 0 {
assert!(!blob.is_null(),
"unexpected SQLITE_BLOB column type with NULL data");
ValueRef::Blob(unsafe { from_raw_parts(blob as *const u8, len as usize) })
} else {
// The return value from sqlite3_column_blob() for a zero-length BLOB
// is a NULL pointer.
ValueRef::Blob(&[])
}
}
_ => unreachable!("sqlite3_column_type returned invalid value"),
}
}
fn step(&self) -> Result<bool> {
match self.stmt.step() {
ffi::SQLITE_ROW => Ok(true),
ffi::SQLITE_DONE => Ok(false),
code => Err(self.conn.decode_result(code).unwrap_err()),
}
}
fn reset(&self) -> c_int {
self.stmt.reset()
}
}
#[cfg(test)]
mod test {
use {Connection, Error, Result};
#[test]
fn test_execute_named() {
let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap();
assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)]).unwrap(),
1);
assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)]).unwrap(),
1);
assert_eq!(3i32,
db.query_row_named::<i32, _>("SELECT SUM(x) FROM foo WHERE x > :x",
&[(":x", &0i32)],
|r| r.get(0))
.unwrap());
}
#[test]
fn test_stmt_execute_named() {
let db = Connection::open_in_memory().unwrap();
let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \
INTEGER)";
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("INSERT INTO test (name) VALUES (:name)").unwrap();
stmt.execute_named(&[(":name", &"one")]).unwrap();
assert_eq!(1i32,
db.query_row_named::<i32, _>("SELECT COUNT(*) FROM test WHERE name = :name",
&[(":name", &"one")],
|r| r.get(0))
.unwrap());
}
#[test]
fn test_query_named() {
let db = Connection::open_in_memory().unwrap();
let sql = r#"
CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER);
INSERT INTO test(id, name) VALUES (1, "one");
"#;
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT id FROM test where name = :name").unwrap();
let mut rows = stmt.query_named(&[(":name", &"one")]).unwrap();
let id: i32 = rows.next().unwrap().unwrap().get(0);
assert_eq!(1, id);
}
#[test]
fn test_query_map_named() {
let db = Connection::open_in_memory().unwrap();
let sql = r#"
CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER);
INSERT INTO test(id, name) VALUES (1, "one");
"#;
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT id FROM test where name = :name").unwrap();
let mut rows = stmt.query_map_named(&[(":name", &"one")], |row| {
let id: i32 = row.get(0);
2 * id
})
.unwrap();
let doubled_id: i32 = rows.next().unwrap().unwrap();
assert_eq!(2, doubled_id);
}
#[test]
fn test_query_and_then_named() {
let db = Connection::open_in_memory().unwrap();
let sql = r#"
CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER);
INSERT INTO test(id, name) VALUES (1, "one");
INSERT INTO test(id, name) VALUES (2, "one");
"#;
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT id FROM test where name = :name ORDER BY id ASC")
.unwrap();
let mut rows = stmt.query_and_then_named(&[(":name", &"one")], |row| {
let id: i32 = row.get(0);
if id == 1 {
Ok(id)
} else {
Err(Error::SqliteSingleThreadedMode)
}
})
.unwrap();
// first row should be Ok
let doubled_id: i32 = rows.next().unwrap().unwrap();
assert_eq!(1, doubled_id);
// second row should be Err
match rows.next().unwrap() {
Ok(_) => panic!("invalid Ok"),
Err(Error::SqliteSingleThreadedMode) => (),
Err(_) => panic!("invalid Err"),
}
}
#[test]
fn test_unbound_parameters_are_null() {
let db = Connection::open_in_memory().unwrap();
let sql = "CREATE TABLE test (x TEXT, y TEXT)";
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)").unwrap();
stmt.execute_named(&[(":x", &"one")]).unwrap();
let result: Option<String> =
db.query_row("SELECT y FROM test WHERE x = 'one'", &[], |row| row.get(0))
.unwrap();
assert!(result.is_none());
}
#[test]
fn test_unbound_parameters_are_reused() {
let db = Connection::open_in_memory().unwrap();
let sql = "CREATE TABLE test (x TEXT, y TEXT)";
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)").unwrap();
stmt.execute_named(&[(":x", &"one")]).unwrap();
stmt.execute_named(&[(":y", &"two")]).unwrap();
let result: String =
db.query_row("SELECT x FROM test WHERE y = 'two'", &[], |row| row.get(0))
.unwrap();
assert_eq!(result, "one");
}
#[test]
fn test_insert() {
let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo(x INTEGER UNIQUE)").unwrap();
let mut stmt = db.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?)").unwrap();
assert_eq!(stmt.insert(&[&1i32]).unwrap(), 1);
assert_eq!(stmt.insert(&[&2i32]).unwrap(), 2);
match stmt.insert(&[&1i32]).unwrap_err() {
Error::StatementChangedRows(0) => (),
err => panic!("Unexpected error {}", err),
}
let mut multi = db.prepare("INSERT INTO foo (x) SELECT 3 UNION ALL SELECT 4").unwrap();
match multi.insert(&[]).unwrap_err() {
Error::StatementChangedRows(2) => (),
err => panic!("Unexpected error {}", err),
}
}
#[test]
fn test_insert_different_tables() {
// Test for https://github.com/jgallagher/rusqlite/issues/171
let db = Connection::open_in_memory().unwrap();
db.execute_batch(r"
CREATE TABLE foo(x INTEGER);
CREATE TABLE bar(x INTEGER);
")
.unwrap();
assert_eq!(db.prepare("INSERT INTO foo VALUES (10)").unwrap().insert(&[]).unwrap(),
1);
assert_eq!(db.prepare("INSERT INTO bar VALUES (10)").unwrap().insert(&[]).unwrap(),
1);
}
#[test]
fn test_exists() {
let db = Connection::open_in_memory().unwrap();
let sql = "BEGIN;
CREATE TABLE foo(x INTEGER);
INSERT INTO foo VALUES(1);
INSERT INTO foo VALUES(2);
END;";
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT 1 FROM foo WHERE x = ?").unwrap();
assert!(stmt.exists(&[&1i32]).unwrap());
assert!(stmt.exists(&[&2i32]).unwrap());
assert!(!stmt.exists(&[&0i32]).unwrap());
}
#[test]
fn test_query_row() {
let db = Connection::open_in_memory().unwrap();
let sql = "BEGIN;
CREATE TABLE foo(x INTEGER, y INTEGER);
INSERT INTO foo VALUES(1, 3);
INSERT INTO foo VALUES(2, 4);
END;";
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT y FROM foo WHERE x = ?").unwrap();
let y: Result<i64> = stmt.query_row(&[&1i32], |r| r.get(0));
assert_eq!(3i64, y.unwrap());
}
}

View File

@@ -1,6 +1,6 @@
//! Tracing and profiling functions. Error and warning log.
use libc::{c_char, c_int, c_void};
use std::os::raw::{c_char, c_int, c_void};
use std::ffi::{CStr, CString};
use std::mem;
use std::ptr;

View File

@@ -427,7 +427,8 @@ mod test {
{
let tx = db.transaction().unwrap();
assert_eq!(2i32,
tx.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap());
tx.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
.unwrap());
}
}
@@ -453,7 +454,8 @@ mod test {
{
let tx = db.transaction().unwrap();
assert_eq!(6i32,
tx.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap());
tx.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
.unwrap());
}
}
@@ -547,7 +549,7 @@ mod test {
}
fn assert_current_sum(x: i32, conn: &Connection) {
let i = conn.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap();
let i = conn.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap();
assert_eq!(x, i);
}
}

View File

@@ -5,7 +5,7 @@ use std::borrow::Cow;
use self::chrono::{NaiveDate, NaiveTime, NaiveDateTime, DateTime, TimeZone, UTC, Local};
use ::Result;
use Result;
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
/// ISO 8601 calendar date without timezone => "YYYY-MM-DD"
@@ -104,9 +104,8 @@ impl FromSql for DateTime<UTC> {
Cow::Borrowed(s)
};
match DateTime::parse_from_rfc3339(&s) {
Ok(dt) => return Ok(dt.with_timezone(&UTC)),
Err(_) => (),
if let Ok(dt) = DateTime::parse_from_rfc3339(&s) {
return Ok(dt.with_timezone(&UTC));
}
}

View File

@@ -5,9 +5,13 @@ use std::fmt;
/// Enum listing possible errors from `FromSql` trait.
#[derive(Debug)]
pub enum FromSqlError {
/// Error when an SQLite value is requested, but the type of the result cannot be converted to the
/// requested Rust type.
/// Error when an SQLite value is requested, but the type of the result cannot be converted to
/// the requested Rust type.
InvalidType,
/// Error when the i64 value returned by SQLite cannot be stored into the requested type.
OutOfRange(i64),
/// An error case available for implementors of the `FromSql` trait.
Other(Box<Error + Send + Sync>),
}
@@ -16,6 +20,7 @@ impl fmt::Display for FromSqlError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
FromSqlError::InvalidType => write!(f, "Invalid type"),
FromSqlError::OutOfRange(i) => write!(f, "Value {} out of range", i),
FromSqlError::Other(ref err) => err.fmt(f),
}
}
@@ -25,6 +30,7 @@ impl Error for FromSqlError {
fn description(&self) -> &str {
match *self {
FromSqlError::InvalidType => "invalid type",
FromSqlError::OutOfRange(_) => "value out of range",
FromSqlError::Other(ref err) => err.description(),
}
}
@@ -32,8 +38,9 @@ impl Error for FromSqlError {
#[cfg_attr(feature="clippy", allow(match_same_arms))]
fn cause(&self) -> Option<&Error> {
match *self {
FromSqlError::InvalidType => None,
FromSqlError::Other(ref err) => err.cause(),
FromSqlError::InvalidType |
FromSqlError::OutOfRange(_) => None,
}
}
}
@@ -46,11 +53,28 @@ pub trait FromSql: Sized {
fn column_result(value: ValueRef) -> FromSqlResult<Self>;
}
impl FromSql for i32 {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
i64::column_result(value).map(|i| i as i32)
}
}
macro_rules! from_sql_integral(
($t:ident) => (
impl FromSql for $t {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
i64::column_result(value).and_then(|i| {
if i < $t::min_value() as i64 || i > $t::max_value() as i64 {
Err(FromSqlError::OutOfRange(i))
} else {
Ok(i as $t)
}
})
}
}
)
);
from_sql_integral!(i8);
from_sql_integral!(i16);
from_sql_integral!(i32);
from_sql_integral!(u8);
from_sql_integral!(u16);
from_sql_integral!(u32);
impl FromSql for i64 {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
@@ -103,3 +127,45 @@ impl FromSql for Value {
Ok(value.into())
}
}
#[cfg(test)]
mod test {
use {Connection, Error};
use super::FromSql;
fn checked_memory_handle() -> Connection {
Connection::open_in_memory().unwrap()
}
#[test]
fn test_integral_ranges() {
let db = checked_memory_handle();
fn check_ranges<T>(db: &Connection, out_of_range: &[i64], in_range: &[i64])
where T: Into<i64> + FromSql + ::std::fmt::Debug
{
for n in out_of_range {
let err = db.query_row("SELECT ?", &[n], |r| r.get_checked::<_, T>(0))
.unwrap()
.unwrap_err();
match err {
Error::IntegralValueOutOfRange(_, value) => assert_eq!(*n, value),
_ => panic!("unexpected error: {}", err),
}
}
for n in in_range {
assert_eq!(*n,
db.query_row("SELECT ?", &[n], |r| r.get::<_, T>(0)).unwrap().into());
}
}
check_ranges::<i8>(&db, &[-129, 128], &[-128, 0, 1, 127]);
check_ranges::<i16>(&db, &[-32769, 32768], &[-32768, -1, 0, 1, 32767]);
check_ranges::<i32>(&db,
&[-2147483649, 2147483648],
&[-2147483648, -1, 0, 1, 2147483647]);
check_ranges::<u8>(&db, &[-2, -1, 256], &[0, 1, 255]);
check_ranges::<u16>(&db, &[-2, -1, 65536], &[0, 1, 65535]);
check_ranges::<u32>(&db, &[-2, -1, 4294967296], &[0, 1, 4294967295]);
}
}

View File

@@ -17,19 +17,17 @@
//! truncates timespecs to the nearest second. If you want different storage for timespecs, you can
//! use a newtype. For example, to store timespecs as `f64`s:
//!
//! ```rust,ignore
//! ```rust
//! extern crate rusqlite;
//! extern crate libc;
//! extern crate time;
//!
//! use rusqlite::types::{FromSql, ToSql, sqlite3_stmt};
//! use rusqlite::types::{FromSql, FromSqlResult, ValueRef, ToSql, ToSqlOutput};
//! use rusqlite::{Result};
//! use libc::c_int;
//! use time;
//!
//! pub struct TimespecSql(pub time::Timespec);
//!
//! impl FromSql for TimespecSql {
//! fn column_result(value: ValueRef) -> Result<Self> {
//! fn column_result(value: ValueRef) -> FromSqlResult<Self> {
//! f64::column_result(value).map(|as_f64| {
//! TimespecSql(time::Timespec{ sec: as_f64.trunc() as i64,
//! nsec: (as_f64.fract() * 1.0e9) as i32 })
@@ -38,12 +36,15 @@
//! }
//!
//! impl ToSql for TimespecSql {
//! unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
//! fn to_sql(&self) -> Result<ToSqlOutput> {
//! let TimespecSql(ts) = *self;
//! let as_f64 = ts.sec as f64 + (ts.nsec as f64) / 1.0e9;
//! as_f64.bind_parameter(stmt, col)
//! Ok(as_f64.into())
//! }
//! }
//!
//! # // Prevent this doc test from being wrapped in a `fn main()` so that it will compile.
//! # fn main() {}
//! ```
//!
//! `ToSql` and `FromSql` are also implemented for `Option<T>` where `T` implements `ToSql` or
@@ -72,11 +73,10 @@ mod serde_json;
/// ## Example
///
/// ```rust,no_run
/// # extern crate libc;
/// # extern crate rusqlite;
/// # use rusqlite::{Connection, Result};
/// # use rusqlite::types::{Null};
/// # use libc::{c_int};
/// # use std::os::raw::{c_int};
/// fn main() {
/// }
/// fn insert_null(conn: &Connection) -> Result<c_int> {
@@ -113,7 +113,7 @@ mod test {
use Connection;
use Error;
use libc::{c_int, c_double};
use std::os::raw::{c_int, c_double};
use std::f64::EPSILON;
use super::Value;
@@ -174,7 +174,7 @@ mod test {
db.execute("INSERT INTO foo(i) VALUES (?)", &[&Value::Integer(10)]).unwrap();
assert_eq!(10i64,
db.query_row("SELECT i FROM foo", &[], |r| r.get(0)).unwrap());
db.query_row::<i64, _>("SELECT i FROM foo", &[], |r| r.get(0)).unwrap());
}
#[test]

View File

@@ -3,7 +3,7 @@ extern crate serde_json;
use self::serde_json::Value;
use ::Result;
use Result;
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
/// Serialize JSON `Value` to text.
@@ -17,8 +17,8 @@ impl ToSql for Value {
impl FromSql for Value {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
match value {
ValueRef::Text(ref s) => serde_json::from_str(s),
ValueRef::Blob(ref b) => serde_json::from_slice(b),
ValueRef::Text(s) => serde_json::from_str(s),
ValueRef::Blob(b) => serde_json::from_slice(b),
_ => return Err(FromSqlError::InvalidType),
}
.map_err(|err| FromSqlError::Other(Box::new(err)))

View File

@@ -1,7 +1,8 @@
use super::{Null, Value, ValueRef};
use ::Result;
use Result;
/// `ToSqlOutput` represents the possible output types for implementors of the `ToSql` trait.
#[derive(Clone,Debug,PartialEq)]
pub enum ToSqlOutput<'a> {
/// A borrowed SQLite-representable value.
Borrowed(ValueRef<'a>),
@@ -28,6 +29,18 @@ impl<'a, T: Into<Value>> From<T> for ToSqlOutput<'a> {
}
}
impl<'a> ToSql for ToSqlOutput<'a> {
fn to_sql(&self) -> Result<ToSqlOutput> {
Ok(match *self {
ToSqlOutput::Borrowed(v) => ToSqlOutput::Borrowed(v),
ToSqlOutput::Owned(ref v) => ToSqlOutput::Borrowed(ValueRef::from(v)),
#[cfg(feature = "blob")]
ToSqlOutput::ZeroBlob(i) => ToSqlOutput::ZeroBlob(i),
})
}
}
/// A trait for types that can be converted into SQLite values.
pub trait ToSql {
fn to_sql(&self) -> Result<ToSqlOutput>;
@@ -57,8 +70,13 @@ macro_rules! to_sql_self(
to_sql_self!(Null);
to_sql_self!(bool);
to_sql_self!(i8);
to_sql_self!(i16);
to_sql_self!(i32);
to_sql_self!(i64);
to_sql_self!(u8);
to_sql_self!(u16);
to_sql_self!(u32);
to_sql_self!(f64);
impl<'a, T: ?Sized> ToSql for &'a T
@@ -95,3 +113,21 @@ impl<T: ToSql> ToSql for Option<T> {
}
}
}
#[cfg(test)]
mod test {
use super::ToSql;
fn is_to_sql<T: ToSql>() {}
#[test]
fn test_integral_types() {
is_to_sql::<i8>();
is_to_sql::<i16>();
is_to_sql::<i32>();
is_to_sql::<i64>();
is_to_sql::<u8>();
is_to_sql::<u16>();
is_to_sql::<u32>();
}
}

View File

@@ -30,11 +30,22 @@ impl From<bool> for Value {
}
}
impl From<i32> for Value {
fn from(i: i32) -> Value {
Value::Integer(i as i64)
}
}
macro_rules! from_i64(
($t:ty) => (
impl From<$t> for Value {
fn from(i: $t) -> Value {
Value::Integer(i as i64)
}
}
)
);
from_i64!(i8);
from_i64!(i16);
from_i64!(i32);
from_i64!(u8);
from_i64!(u16);
from_i64!(u32);
impl From<i64> for Value {
fn from(i: i64) -> Value {

View File

@@ -1,4 +1,4 @@
use ::types::{FromSqlError, FromSqlResult};
use types::{FromSqlError, FromSqlResult};
use super::{Value, Type};
/// A non-owning [dynamic type value](http://sqlite.org/datatype3.html). Typically the
@@ -54,7 +54,7 @@ impl<'a> ValueRef<'a> {
/// `Err(Error::InvalidColumnType)`.
pub fn as_str(&self) -> FromSqlResult<&str> {
match *self {
ValueRef::Text(ref t) => Ok(t),
ValueRef::Text(t) => Ok(t),
_ => Err(FromSqlError::InvalidType),
}
}
@@ -63,7 +63,7 @@ impl<'a> ValueRef<'a> {
/// `Err(Error::InvalidColumnType)`.
pub fn as_blob(&self) -> FromSqlResult<&[u8]> {
match *self {
ValueRef::Blob(ref b) => Ok(b),
ValueRef::Blob(b) => Ok(b),
_ => Err(FromSqlError::InvalidType),
}
}

17
src/version.rs Normal file
View File

@@ -0,0 +1,17 @@
use ffi;
use std::ffi::CStr;
/// Returns the SQLite version as an integer; e.g., `3016002` for version 3.16.2.
///
/// See [sqlite3_libversion_number()](https://www.sqlite.org/c3ref/libversion.html).
pub fn version_number() -> i32 {
unsafe { ffi::sqlite3_libversion_number() }
}
/// Returns the SQLite version as a string; e.g., `"3.16.2"` for version 3.16.2.
///
/// See [sqlite3_libversion()](https://www.sqlite.org/c3ref/libversion.html).
pub fn version() -> &'static str {
let cstr = unsafe { CStr::from_ptr(ffi::sqlite3_libversion()) };
cstr.to_str().expect("SQLite version string is not valid UTF8 ?!")
}

View File

@@ -1,10 +1,10 @@
//! CSV Virtual Table
extern crate csv;
use std::fs::File;
use std::os::raw::{c_char, c_int, c_void};
use std::path::Path;
use std::result;
use std::str;
use libc;
use {Connection, Error, Result};
use ffi;
@@ -58,7 +58,7 @@ impl CSVTab {
}
impl VTab<CSVTabCursor> for CSVTab {
fn connect(db: *mut ffi::sqlite3, _aux: *mut libc::c_void, args: &[&[u8]]) -> Result<CSVTab> {
fn connect(db: *mut ffi::sqlite3, _aux: *mut c_void, args: &[&[u8]]) -> Result<CSVTab> {
if args.len() < 4 {
return Err(Error::ModuleError("no CSV file specified".to_owned()));
}
@@ -173,7 +173,7 @@ impl VTabCursor<CSVTab> for CSVTabCursor {
}
fn filter(&mut self,
_idx_num: libc::c_int,
_idx_num: c_int,
_idx_str: Option<&str>,
_args: &Values)
-> Result<()> {
@@ -203,7 +203,7 @@ impl VTabCursor<CSVTab> for CSVTabCursor {
fn eof(&self) -> bool {
self.eof
}
fn column(&self, ctx: &mut Context, col: libc::c_int) -> Result<()> {
fn column(&self, ctx: &mut Context, col: c_int) -> Result<()> {
if col < 0 || col as usize >= self.cols.len() {
return Err(Error::ModuleError(format!("column index out of bounds: {}", col)));
}

View File

@@ -3,8 +3,8 @@
use std::cell::RefCell;
use std::default::Default;
use std::mem;
use std::os::raw::{c_char, c_int, c_void};
use std::rc::Rc;
use libc;
use {Connection, Error, Result};
use ffi;
@@ -62,7 +62,7 @@ struct IntArrayVTab {
impl VTab<IntArrayVTabCursor> for IntArrayVTab {
fn connect(db: *mut ffi::sqlite3,
aux: *mut libc::c_void,
aux: *mut c_void,
_args: &[&[u8]])
-> Result<IntArrayVTab> {
let array = unsafe { mem::transmute(aux) };
@@ -106,7 +106,7 @@ impl VTabCursor<IntArrayVTab> for IntArrayVTabCursor {
unsafe { &mut *(self.base.pVtab as *mut IntArrayVTab) }
}
fn filter(&mut self,
_idx_num: libc::c_int,
_idx_num: c_int,
_idx_str: Option<&str>,
_args: &Values)
-> Result<()> {
@@ -124,7 +124,7 @@ impl VTabCursor<IntArrayVTab> for IntArrayVTabCursor {
self.i >= array.len()
}
}
fn column(&self, ctx: &mut Context, _i: libc::c_int) -> Result<()> {
fn column(&self, ctx: &mut Context, _i: c_int) -> Result<()> {
let vtab = self.vtab();
unsafe {
let array = (*vtab.array).borrow();
@@ -170,8 +170,8 @@ mod test {
s.query_map(&[], |row| {
let i1: i64 = row.get(0);
assert!(i1 == 1 || i1 == 3);
assert_eq!(11, row.get(1));
assert_eq!(-5, row.get(2));
assert_eq!(11, row.get::<i32, i32>(1));
assert_eq!(-5, row.get::<i32, i32>(2));
}).unwrap();
}
@@ -185,9 +185,9 @@ mod test {
{
let mut rows = s.query(&[]).unwrap();
let row = rows.next().unwrap().unwrap();
assert_eq!(1, row.get(0));
assert_eq!(11, row.get(1));
assert_eq!(-5, row.get(2));
assert_eq!(1, row.get::<i32, i32>(0));
assert_eq!(11, row.get::<i32, i32>(1));
assert_eq!(-5, row.get::<i32, i32>(2));
}
p2.borrow_mut().clear();

View File

@@ -3,9 +3,9 @@
use std::borrow::Cow::{self, Borrowed, Owned};
use std::ffi::CString;
use std::mem;
use std::os::raw::{c_char, c_int, c_void};
use std::ptr;
use std::slice;
use libc;
use {Connection, Error, Result, InnerConnection, str_to_cstring};
use error::error_from_sqlite_code;
@@ -46,7 +46,7 @@ pub trait VTab<C: VTabCursor<Self>>: Sized {
/// Create a new instance of a virtual table in response to a CREATE VIRTUAL TABLE statement.
/// The `db` parameter is a pointer to the SQLite database connection that is executing
/// the CREATE VIRTUAL TABLE statement.
fn connect(db: *mut ffi::sqlite3, aux: *mut libc::c_void, args: &[&[u8]]) -> Result<Self>;
fn connect(db: *mut ffi::sqlite3, aux: *mut c_void, args: &[&[u8]]) -> Result<Self>;
/// Determine the best way to access the virtual table.
fn best_index(&self, info: &mut IndexInfo) -> Result<()>;
/// Create a new cursor used for accessing a virtual table.
@@ -56,7 +56,7 @@ pub trait VTab<C: VTabCursor<Self>>: Sized {
bitflags! {
#[doc = "Index constraint operator."]
#[repr(C)]
pub flags IndexConstraintOp: ::libc::c_uchar {
pub flags IndexConstraintOp: ::std::os::raw::c_uchar {
const SQLITE_INDEX_CONSTRAINT_EQ = 2,
const SQLITE_INDEX_CONSTRAINT_GT = 4,
const SQLITE_INDEX_CONSTRAINT_LE = 8,
@@ -80,7 +80,7 @@ impl IndexInfo {
unsafe { (*self.0).nOrderBy as usize }
}
/// Column number
pub fn order_by_column(&self, order_by_idx: usize) -> libc::c_int {
pub fn order_by_column(&self, order_by_idx: usize) -> c_int {
unsafe {
let order_bys = slice::from_raw_parts((*self.0).aOrderBy, (*self.0).nOrderBy as usize);
order_bys[order_by_idx].iColumn
@@ -103,7 +103,7 @@ impl IndexInfo {
}
/// Number used to identify the index
pub fn set_idx_num(&mut self, idx_num: libc::c_int) {
pub fn set_idx_num(&mut self, idx_num: c_int) {
unsafe {
(*self.0).idxNum = idx_num;
}
@@ -147,7 +147,7 @@ pub struct IndexConstraint<'a>(&'a ffi::sqlite3_index_constraint);
impl<'a> IndexConstraint<'a> {
/// Column constrained. -1 for ROWID
pub fn column(&self) -> libc::c_int {
pub fn column(&self) -> c_int {
self.0.iColumn
}
/// Constraint operator
@@ -164,7 +164,7 @@ pub struct IndexConstraintUsage<'a>(&'a mut ffi::sqlite3_index_constraint_usage)
impl<'a> IndexConstraintUsage<'a> {
/// if `argv_index` > 0, constraint is part of argv to xFilter
pub fn set_argv_index(&mut self, argv_index: libc::c_int) {
pub fn set_argv_index(&mut self, argv_index: c_int) {
self.0.argvIndex = argv_index;
}
/// if `omit`, do not code a test for this constraint
@@ -178,7 +178,7 @@ pub trait VTabCursor<V: VTab<Self>>: Sized {
/// Accessor to the associated virtual table.
fn vtab(&self) -> &mut V;
/// Begin a search of a virtual table.
fn filter(&mut self, idx_num: libc::c_int, idx_str: Option<&str>, args: &Values) -> Result<()>;
fn filter(&mut self, idx_num: c_int, idx_str: Option<&str>, args: &Values) -> Result<()>;
/// Advance cursor to the next row of a result set initiated by `filter`.
fn next(&mut self) -> Result<()>;
/// Must return `false` if the cursor currently points to a valid row of data,
@@ -187,7 +187,7 @@ pub trait VTabCursor<V: VTab<Self>>: Sized {
/// Find the value for the `i`-th column of the current row.
/// `i` is zero-based so the first column is numbered 0.
/// May return its result back to SQLite using one of the specified `ctx`.
fn column(&self, ctx: &mut Context, i: libc::c_int) -> Result<()>;
fn column(&self, ctx: &mut Context, i: c_int) -> Result<()>;
/// Return the rowid of row that the cursor is currently pointing at.
fn rowid(&self) -> Result<i64>;
}
@@ -228,6 +228,7 @@ impl<'a> Values<'a> {
FromSqlError::Other(err) => {
Error::FromSqlConversionFailure(idx, value.data_type(), err)
}
FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx as c_int, i),
})
}
@@ -326,7 +327,7 @@ pub fn escape_double_quote(identifier: &str) -> Cow<str> {
}
// FIXME copy/paste from function.rs
unsafe extern "C" fn free_boxed_value<T>(p: *mut libc::c_void) {
unsafe extern "C" fn free_boxed_value<T>(p: *mut c_void) {
let _: Box<T> = Box::from_raw(mem::transmute(p));
}
@@ -367,12 +368,12 @@ static $module_name: ffi::sqlite3_module = ffi::sqlite3_module {
};
unsafe extern "C" fn $connect(db: *mut ffi::sqlite3,
aux: *mut libc::c_void,
argc: libc::c_int,
argv: *const *const libc::c_char,
aux: *mut c_void,
argc: c_int,
argv: *const *const c_char,
pp_vtab: *mut *mut ffi::sqlite3_vtab,
err_msg: *mut *mut libc::c_char)
-> libc::c_int {
err_msg: *mut *mut c_char)
-> c_int {
use std::error::Error as StdError;
use std::ffi::CStr;
use std::slice;
@@ -401,7 +402,7 @@ unsafe extern "C" fn $connect(db: *mut ffi::sqlite3,
}
unsafe extern "C" fn $best_index(vtab: *mut ffi::sqlite3_vtab,
info: *mut ffi::sqlite3_index_info)
-> libc::c_int {
-> c_int {
use std::error::Error as StdError;
use vtab::set_err_msg;
let vt = vtab as *mut $vtab;
@@ -422,7 +423,7 @@ unsafe extern "C" fn $best_index(vtab: *mut ffi::sqlite3_vtab,
}
}
unsafe extern "C" fn $disconnect(vtab: *mut ffi::sqlite3_vtab) -> libc::c_int {
unsafe extern "C" fn $disconnect(vtab: *mut ffi::sqlite3_vtab) -> c_int {
let vtab = vtab as *mut $vtab;
let _: Box<$vtab> = Box::from_raw(vtab);
ffi::SQLITE_OK
@@ -430,7 +431,7 @@ unsafe extern "C" fn $disconnect(vtab: *mut ffi::sqlite3_vtab) -> libc::c_int {
unsafe extern "C" fn $open(vtab: *mut ffi::sqlite3_vtab,
pp_cursor: *mut *mut ffi::sqlite3_vtab_cursor)
-> libc::c_int {
-> c_int {
use std::error::Error as StdError;
use vtab::set_err_msg;
let vt = vtab as *mut $vtab;
@@ -452,18 +453,18 @@ unsafe extern "C" fn $open(vtab: *mut ffi::sqlite3_vtab,
}
}
}
unsafe extern "C" fn $close(cursor: *mut ffi::sqlite3_vtab_cursor) -> libc::c_int {
unsafe extern "C" fn $close(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int {
let cr = cursor as *mut $cursor;
let _: Box<$cursor> = Box::from_raw(cr);
ffi::SQLITE_OK
}
unsafe extern "C" fn $filter(cursor: *mut ffi::sqlite3_vtab_cursor,
idx_num: libc::c_int,
idx_str: *const libc::c_char,
argc: libc::c_int,
idx_num: c_int,
idx_str: *const c_char,
argc: c_int,
argv: *mut *mut ffi::sqlite3_value)
-> libc::c_int {
-> c_int {
use std::ffi::CStr;
use std::slice;
use std::str;
@@ -479,19 +480,19 @@ unsafe extern "C" fn $filter(cursor: *mut ffi::sqlite3_vtab_cursor,
let cr = cursor as *mut $cursor;
cursor_error(cursor, (*cr).filter(idx_num, idx_name, &values))
}
unsafe extern "C" fn $next(cursor: *mut ffi::sqlite3_vtab_cursor) -> libc::c_int {
unsafe extern "C" fn $next(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int {
use vtab::cursor_error;
let cr = cursor as *mut $cursor;
cursor_error(cursor, (*cr).next())
}
unsafe extern "C" fn $eof(cursor: *mut ffi::sqlite3_vtab_cursor) -> libc::c_int {
unsafe extern "C" fn $eof(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int {
let cr = cursor as *mut $cursor;
(*cr).eof() as libc::c_int
(*cr).eof() as c_int
}
unsafe extern "C" fn $column(cursor: *mut ffi::sqlite3_vtab_cursor,
ctx: *mut ffi::sqlite3_context,
i: libc::c_int)
-> libc::c_int {
i: c_int)
-> c_int {
use vtab::{result_error, Context};
let cr = cursor as *mut $cursor;
let mut ctxt = Context(ctx);
@@ -499,7 +500,7 @@ unsafe extern "C" fn $column(cursor: *mut ffi::sqlite3_vtab_cursor,
}
unsafe extern "C" fn $rowid(cursor: *mut ffi::sqlite3_vtab_cursor,
p_rowid: *mut ffi::sqlite3_int64)
-> libc::c_int {
-> c_int {
use vtab::cursor_error;
let cr = cursor as *mut $cursor;
match (*cr).rowid() {
@@ -516,7 +517,7 @@ unsafe extern "C" fn $rowid(cursor: *mut ffi::sqlite3_vtab_cursor,
/// Virtual table cursors can set an error message by assigning a string to `zErrMsg`.
pub unsafe fn cursor_error<T>(cursor: *mut ffi::sqlite3_vtab_cursor,
result: Result<T>)
-> libc::c_int {
-> c_int {
use std::error::Error as StdError;
match result {
Ok(_) => ffi::SQLITE_OK,
@@ -536,14 +537,14 @@ pub unsafe fn cursor_error<T>(cursor: *mut ffi::sqlite3_vtab_cursor,
/// Virtual tables methods can set an error message by assigning a string to `zErrMsg`.
pub unsafe fn set_err_msg(vtab: *mut ffi::sqlite3_vtab, err_msg: &str) {
if !(*vtab).zErrMsg.is_null() {
ffi::sqlite3_free((*vtab).zErrMsg as *mut libc::c_void);
ffi::sqlite3_free((*vtab).zErrMsg as *mut c_void);
}
(*vtab).zErrMsg = mprintf(err_msg);
}
/// To raise an error, the `column` method should use this method to set the error message
/// and return the error code.
unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) -> libc::c_int {
unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) -> c_int {
use std::error::Error as StdError;
match result {
Ok(_) => ffi::SQLITE_OK,
@@ -576,7 +577,7 @@ unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) ->
// Space to hold this error message string must be obtained
// from an SQLite memory allocation function.
pub fn mprintf(err_msg: &str) -> *mut ::libc::c_char {
pub fn mprintf(err_msg: &str) -> *mut c_char {
let c_format = CString::new("%s").unwrap();
let c_err = CString::new(err_msg).unwrap();
unsafe { ffi::sqlite3_mprintf(c_format.as_ptr(), c_err.as_ptr()) }

View File

@@ -1,7 +1,7 @@
//! generate series virtual table.
//! Port of C [generate series "function"](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/series.c).
use std::default::Default;
use libc;
use std::os::raw::{c_char, c_int, c_void};
use {Connection, Error, Result};
use ffi;
@@ -30,14 +30,14 @@ init_module!(SERIES_MODULE,
series_rowid);
// Column numbers
// const SERIES_COLUMN_VALUE : libc::c_int = 0;
const SERIES_COLUMN_START: libc::c_int = 1;
const SERIES_COLUMN_STOP: libc::c_int = 2;
const SERIES_COLUMN_STEP: libc::c_int = 3;
// const SERIES_COLUMN_VALUE : c_int = 0;
const SERIES_COLUMN_START: c_int = 1;
const SERIES_COLUMN_STOP: c_int = 2;
const SERIES_COLUMN_STEP: c_int = 3;
bitflags! {
#[repr(C)]
flags QueryPlanFlags: ::libc::c_int {
flags QueryPlanFlags: ::std::os::raw::c_int {
// start = $value -- constraint exists
const START = 1,
// stop = $value -- constraint exists
@@ -62,7 +62,7 @@ struct SeriesTab {
impl VTab<SeriesTabCursor> for SeriesTab {
fn connect(db: *mut ffi::sqlite3,
_aux: *mut libc::c_void,
_aux: *mut c_void,
_args: &[&[u8]])
-> Result<SeriesTab> {
let vtab = SeriesTab { base: Default::default() };
@@ -176,7 +176,7 @@ impl VTabCursor<SeriesTab> for SeriesTabCursor {
unsafe { &mut *(self.base.pVtab as *mut SeriesTab) }
}
fn filter(&mut self,
idx_num: libc::c_int,
idx_num: c_int,
_idx_str: Option<&str>,
args: &Values)
-> Result<()> {
@@ -230,7 +230,7 @@ impl VTabCursor<SeriesTab> for SeriesTabCursor {
self.value > self.max_value
}
}
fn column(&self, ctx: &mut Context, i: libc::c_int) -> Result<()> {
fn column(&self, ctx: &mut Context, i: c_int) -> Result<()> {
let x = match i {
SERIES_COLUMN_START => self.min_value,
SERIES_COLUMN_STOP => self.max_value,
@@ -264,7 +264,7 @@ mod test {
let mut s = db.prepare("SELECT * FROM generate_series(0,20,5)").unwrap();
let series = s.query_map(&[], |row| row.get(0))
let series = s.query_map(&[], |row| row.get::<i32, i32>(0))
.unwrap();
let mut expected = 0;