Eliminate Binding; introduce Parameter

This commit is contained in:
Ivan Ukhov 2015-06-19 11:31:29 -04:00
parent 56c36f7ddc
commit 7441967fbc
3 changed files with 69 additions and 42 deletions

View File

@ -90,10 +90,10 @@ mod statement;
pub use database::Database; pub use database::Database;
pub use error::{Error, ErrorKind}; pub use error::{Error, ErrorKind};
pub use statement::{Statement, Binding, Value, State}; pub use statement::{Statement, State, Parameter, Value};
/// A result. /// A result.
pub type Result<T> = ::std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
/// Open a connection to a new or existing database. /// Open a connection to a new or existing database.
#[inline] #[inline]

View File

@ -10,17 +10,20 @@ pub struct Statement<'l> {
phantom: PhantomData<(ffi::sqlite3_stmt, &'l ffi::sqlite3)>, phantom: PhantomData<(ffi::sqlite3_stmt, &'l ffi::sqlite3)>,
} }
/// A binding of a parameter of a prepared statement. /// A parameter of a prepared statement.
pub enum Binding<'l> { pub trait Parameter {
Float(usize, f64), /// Bind the parameter at a specific location.
Integer(usize, i64), ///
Text(usize, &'l str), /// The leftmost location has the index 1.
fn bind(&self, &Statement, usize) -> Result<()>;
} }
/// A value stored in a result row of a query. /// A value stored in a prepared statement.
pub trait Value { pub trait Value {
/// Read the value stored in a specific column. /// Read the value stored in a specific column.
fn read(statement: &Statement, i: usize) -> Result<Self>; ///
/// The leftmost column has the index 0.
fn read(&Statement, usize) -> Result<Self>;
} }
/// A state of a prepared statement. /// A state of a prepared statement.
@ -31,38 +34,20 @@ pub enum State {
} }
impl<'l> Statement<'l> { impl<'l> Statement<'l> {
/// Bind values to the parameters. /// Bind the parameter at a specific location.
/// ///
/// The leftmost parameter has the index 1. /// The leftmost location has the index 1.
pub fn bind(&mut self, bindings: &[Binding]) -> Result<()> { #[inline]
for binding in bindings.iter() { pub fn bind<P: Parameter>(&mut self, i: usize, parameter: P) -> Result<()> {
match binding { parameter.bind(self, i)
&Binding::Float(i, value) => unsafe {
debug_assert!(i > 0, "the indexing starts from 1");
success!(self.raw.1, ffi::sqlite3_bind_double(self.raw.0, i as c_int,
value as c_double));
},
&Binding::Integer(i, value) => unsafe {
debug_assert!(i > 0, "the indexing starts from 1");
success!(self.raw.1, ffi::sqlite3_bind_int64(self.raw.0, i as c_int,
value as ffi::sqlite3_int64));
},
&Binding::Text(i, value) => unsafe {
debug_assert!(i > 0, "the indexing starts from 1");
success!(self.raw.1, ffi::sqlite3_bind_text(self.raw.0, i as c_int,
str_to_c_str!(value), -1, None));
},
}
}
Ok(())
} }
/// Return the value stored in a specific column of the current result row. /// Read the value stored in a specific column.
/// ///
/// The leftmost column has the index 0. /// The leftmost column has the index 0.
#[inline] #[inline]
pub fn column<T: Value>(&self, i: usize) -> Result<T> { pub fn read<V: Value>(&self, i: usize) -> Result<V> {
<T as Value>::read(self, i) Value::read(self, i)
} }
/// Evaluate the statement. /// Evaluate the statement.
@ -89,19 +74,59 @@ impl<'l> Drop for Statement<'l> {
} }
} }
impl Parameter for f64 {
#[inline]
fn bind(&self, statement: &Statement, i: usize) -> Result<()> {
debug_assert!(i > 0, "the indexing starts from 1");
unsafe {
success!(statement.raw.1, ffi::sqlite3_bind_double(statement.raw.0, i as c_int,
*self as c_double));
}
Ok(())
}
}
impl Parameter for i64 {
#[inline]
fn bind(&self, statement: &Statement, i: usize) -> Result<()> {
debug_assert!(i > 0, "the indexing starts from 1");
unsafe {
success!(statement.raw.1, ffi::sqlite3_bind_int64(statement.raw.0, i as c_int,
*self as ffi::sqlite3_int64));
}
Ok(())
}
}
impl<'l> Parameter for &'l str {
#[inline]
fn bind(&self, statement: &Statement, i: usize) -> Result<()> {
debug_assert!(i > 0, "the indexing starts from 1");
unsafe {
success!(statement.raw.1, ffi::sqlite3_bind_text(statement.raw.0, i as c_int,
str_to_c_str!(self.as_bytes()),
-1, None));
}
Ok(())
}
}
impl Value for f64 { impl Value for f64 {
#[inline]
fn read(statement: &Statement, i: usize) -> Result<f64> { fn read(statement: &Statement, i: usize) -> Result<f64> {
Ok(unsafe { ffi::sqlite3_column_double(statement.raw.0, i as c_int) as f64 }) Ok(unsafe { ffi::sqlite3_column_double(statement.raw.0, i as c_int) as f64 })
} }
} }
impl Value for i64 { impl Value for i64 {
#[inline]
fn read(statement: &Statement, i: usize) -> Result<i64> { fn read(statement: &Statement, i: usize) -> Result<i64> {
Ok(unsafe { ffi::sqlite3_column_int64(statement.raw.0, i as c_int) as i64 }) Ok(unsafe { ffi::sqlite3_column_int64(statement.raw.0, i as c_int) as i64 })
} }
} }
impl Value for String { impl Value for String {
#[inline]
fn read(statement: &Statement, i: usize) -> Result<String> { fn read(statement: &Statement, i: usize) -> Result<String> {
unsafe { unsafe {
let pointer = ffi::sqlite3_column_text(statement.raw.0, i as c_int); let pointer = ffi::sqlite3_column_text(statement.raw.0, i as c_int);

View File

@ -7,7 +7,6 @@ macro_rules! ok(
#[test] #[test]
fn workflow() { fn workflow() {
use sqlite::Binding::*;
use sqlite::State; use sqlite::State;
macro_rules! pair( macro_rules! pair(
@ -22,7 +21,9 @@ fn workflow() {
{ {
let sql = r#"INSERT INTO `users` (id, name, age) VALUES (?, ?, ?);"#; let sql = r#"INSERT INTO `users` (id, name, age) VALUES (?, ?, ?);"#;
let mut statement = ok!(database.prepare(sql)); let mut statement = ok!(database.prepare(sql));
ok!(statement.bind(&[Integer(1, 1), Text(2, "Alice"), Float(3, 20.99)])); ok!(statement.bind(1, 1));
ok!(statement.bind(2, "Alice"));
ok!(statement.bind(3, 20.99));
assert!(ok!(statement.step()) == State::Done); assert!(ok!(statement.step()) == State::Done);
} }
@ -44,16 +45,15 @@ fn workflow() {
let sql = r#"SELECT * FROM `users`;"#; let sql = r#"SELECT * FROM `users`;"#;
let mut statement = ok!(database.prepare(sql)); let mut statement = ok!(database.prepare(sql));
assert!(ok!(statement.step()) == State::Row); assert!(ok!(statement.step()) == State::Row);
assert!(ok!(statement.column::<i64>(0)) == 1); assert!(ok!(statement.read::<i64>(0)) == 1);
assert!(ok!(statement.column::<String>(1)) == String::from("Alice")); assert!(ok!(statement.read::<String>(1)) == String::from("Alice"));
assert!(ok!(statement.column::<f64>(2)) == 20.99); assert!(ok!(statement.read::<f64>(2)) == 20.99);
assert!(ok!(statement.step()) == State::Done); assert!(ok!(statement.step()) == State::Done);
} }
} }
#[test] #[test]
fn stress() { fn stress() {
use sqlite::Binding::*;
use sqlite::State; use sqlite::State;
use std::path::PathBuf; use std::path::PathBuf;
use std::thread; use std::thread;
@ -73,7 +73,9 @@ fn stress() {
ok!(database.set_busy_handler(|_| true)); ok!(database.set_busy_handler(|_| true));
let sql = r#"INSERT INTO `users` (id, name, age) VALUES (?, ?, ?);"#; let sql = r#"INSERT INTO `users` (id, name, age) VALUES (?, ?, ?);"#;
let mut statement = ok!(database.prepare(sql)); let mut statement = ok!(database.prepare(sql));
ok!(statement.bind(&[Integer(1, 1), Text(2, "Alice"), Float(3, 20.99)])); ok!(statement.bind(1, 1));
ok!(statement.bind(2, "Alice"));
ok!(statement.bind(3, 20.99));
assert!(ok!(statement.step()) == State::Done); assert!(ok!(statement.step()) == State::Done);
true true
}) })