From 7441967fbc23871ee01aa679355660bc595c09da Mon Sep 17 00:00:00 2001 From: Ivan Ukhov Date: Fri, 19 Jun 2015 11:31:29 -0400 Subject: [PATCH] Eliminate Binding; introduce Parameter --- src/lib.rs | 4 +-- src/statement.rs | 91 ++++++++++++++++++++++++++++++------------------ tests/lib.rs | 16 +++++---- 3 files changed, 69 insertions(+), 42 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f91bba5..5d94e2b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -90,10 +90,10 @@ mod statement; pub use database::Database; pub use error::{Error, ErrorKind}; -pub use statement::{Statement, Binding, Value, State}; +pub use statement::{Statement, State, Parameter, Value}; /// A result. -pub type Result = ::std::result::Result; +pub type Result = std::result::Result; /// Open a connection to a new or existing database. #[inline] diff --git a/src/statement.rs b/src/statement.rs index cfd5e44..0e46e2a 100644 --- a/src/statement.rs +++ b/src/statement.rs @@ -10,17 +10,20 @@ pub struct Statement<'l> { phantom: PhantomData<(ffi::sqlite3_stmt, &'l ffi::sqlite3)>, } -/// A binding of a parameter of a prepared statement. -pub enum Binding<'l> { - Float(usize, f64), - Integer(usize, i64), - Text(usize, &'l str), +/// A parameter of a prepared statement. +pub trait Parameter { + /// Bind the parameter at a specific location. + /// + /// 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 { /// Read the value stored in a specific column. - fn read(statement: &Statement, i: usize) -> Result; + /// + /// The leftmost column has the index 0. + fn read(&Statement, usize) -> Result; } /// A state of a prepared statement. @@ -31,38 +34,20 @@ pub enum State { } impl<'l> Statement<'l> { - /// Bind values to the parameters. + /// Bind the parameter at a specific location. /// - /// The leftmost parameter has the index 1. - pub fn bind(&mut self, bindings: &[Binding]) -> Result<()> { - for binding in bindings.iter() { - match binding { - &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(()) + /// The leftmost location has the index 1. + #[inline] + pub fn bind(&mut self, i: usize, parameter: P) -> Result<()> { + parameter.bind(self, i) } - /// 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. #[inline] - pub fn column(&self, i: usize) -> Result { - ::read(self, i) + pub fn read(&self, i: usize) -> Result { + Value::read(self, i) } /// 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 { + #[inline] fn read(statement: &Statement, i: usize) -> Result { Ok(unsafe { ffi::sqlite3_column_double(statement.raw.0, i as c_int) as f64 }) } } impl Value for i64 { + #[inline] fn read(statement: &Statement, i: usize) -> Result { Ok(unsafe { ffi::sqlite3_column_int64(statement.raw.0, i as c_int) as i64 }) } } impl Value for String { + #[inline] fn read(statement: &Statement, i: usize) -> Result { unsafe { let pointer = ffi::sqlite3_column_text(statement.raw.0, i as c_int); diff --git a/tests/lib.rs b/tests/lib.rs index 1cfec3b..711d510 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -7,7 +7,6 @@ macro_rules! ok( #[test] fn workflow() { - use sqlite::Binding::*; use sqlite::State; macro_rules! pair( @@ -22,7 +21,9 @@ fn workflow() { { let sql = r#"INSERT INTO `users` (id, name, age) VALUES (?, ?, ?);"#; 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); } @@ -44,16 +45,15 @@ fn workflow() { let sql = r#"SELECT * FROM `users`;"#; let mut statement = ok!(database.prepare(sql)); assert!(ok!(statement.step()) == State::Row); - assert!(ok!(statement.column::(0)) == 1); - assert!(ok!(statement.column::(1)) == String::from("Alice")); - assert!(ok!(statement.column::(2)) == 20.99); + assert!(ok!(statement.read::(0)) == 1); + assert!(ok!(statement.read::(1)) == String::from("Alice")); + assert!(ok!(statement.read::(2)) == 20.99); assert!(ok!(statement.step()) == State::Done); } } #[test] fn stress() { - use sqlite::Binding::*; use sqlite::State; use std::path::PathBuf; use std::thread; @@ -73,7 +73,9 @@ fn stress() { ok!(database.set_busy_handler(|_| true)); let sql = r#"INSERT INTO `users` (id, name, age) VALUES (?, ?, ?);"#; 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); true })