Introduce Iterator

This commit is contained in:
Ivan Ukhov 2015-08-02 22:29:04 -04:00
parent c4772f7dd8
commit d89cc13d7b
6 changed files with 115 additions and 28 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "sqlite"
version = "0.17.2"
version = "0.18.0"
authors = ["Ivan Ukhov <ivan.ukhov@gmail.com>"]
license = "MIT"
repository = "https://github.com/stainless-steel/sqlite"

View File

@ -3,9 +3,9 @@ use libc::{c_char, c_int, c_void};
use std::marker::PhantomData;
use std::path::Path;
use {Result, Statement};
use {Iterator, Result, Statement};
/// A connection to a database.
/// A database connection.
pub struct Connection {
raw: *mut ffi::sqlite3,
busy_callback: Option<Box<FnMut(usize) -> bool>>,
@ -24,28 +24,29 @@ impl Connection {
Ok(Connection { raw: raw, busy_callback: None, phantom: PhantomData })
}
/// Execute a query without processing the resulting rows if any.
/// Execute a statement without processing the resulting rows if any.
#[inline]
pub fn execute<T: AsRef<str>>(&self, query: T) -> Result<()> {
pub fn execute<T: AsRef<str>>(&self, statement: T) -> Result<()> {
unsafe {
ok!(self.raw, ffi::sqlite3_exec(self.raw, str_to_cstr!(query.as_ref()).as_ptr(), None,
0 as *mut _, 0 as *mut _));
ok!(self.raw, ffi::sqlite3_exec(self.raw, str_to_cstr!(statement.as_ref()).as_ptr(),
None, 0 as *mut _, 0 as *mut _));
}
Ok(())
}
/// Execute a query and process the resulting rows if any.
/// Execute a statement and process the resulting rows as plain text.
///
/// The callback is triggered for each row. If the callback returns `false`,
/// no more rows will be processed. For large queries and non-string data
/// types, prepared statement are highly preferable; see `prepare`.
/// types, prepared statement are highly preferable; see `iterate` and
/// `prepare`.
#[inline]
pub fn process<T: AsRef<str>, F>(&self, query: T, callback: F) -> Result<()>
pub fn process<T: AsRef<str>, F>(&self, statement: T, callback: F) -> Result<()>
where F: FnMut(&[(&str, Option<&str>)]) -> bool
{
unsafe {
let callback = Box::new(callback);
ok!(self.raw, ffi::sqlite3_exec(self.raw, str_to_cstr!(query.as_ref()).as_ptr(),
ok!(self.raw, ffi::sqlite3_exec(self.raw, str_to_cstr!(statement.as_ref()).as_ptr(),
Some(process_callback::<F>),
&*callback as *const F as *mut F as *mut _,
0 as *mut _));
@ -55,8 +56,15 @@ impl Connection {
/// Create a prepared statement.
#[inline]
pub fn prepare<'l, T: AsRef<str>>(&'l self, query: T) -> Result<Statement<'l>> {
::statement::new(self.raw, query)
pub fn prepare<'l, T: AsRef<str>>(&'l self, statement: T) -> Result<Statement<'l>> {
::statement::new(self.raw, statement)
}
/// Create a reusable iterator over the resulting rows of a prepared
/// statement.
#[inline]
pub fn iterate<'l, T: AsRef<str>>(&'l self, statement: T) -> Result<Iterator<'l>> {
::iterator::new(try!(::statement::new(self.raw, statement)))
}
/// Set a callback for handling busy events.

57
src/iterator.rs Normal file
View File

@ -0,0 +1,57 @@
use statement::{State, Statement, Bindable, Readable};
use {Result, Value};
/// A reusable iterator over the results of a prepared statement.
pub struct Iterator<'l> {
state: Option<State>,
values: Option<Vec<Value>>,
statement: Statement<'l>,
}
impl<'l> Iterator<'l> {
/// Bind parameters and start iterating over the resulting rows.
///
/// The function assigns values to the parameters of the underlaying
/// prepared statement, execute it, and start iterating over the resulting
/// rows.
pub fn start(&mut self, values: &[Value]) -> Result<()> {
try!(self.statement.reset());
for (i, value) in values.iter().enumerate() {
try!(self.statement.bind(i + 1, value));
}
self.state = Some(try!(self.statement.step()));
Ok(())
}
/// Read the next row.
pub fn next(&mut self) -> Result<Option<&[Value]>> {
match self.state {
Some(State::Row) => {},
_ => return Ok(None),
}
let values = match self.values.take() {
Some(mut values) => {
for (i, value) in values.iter_mut().enumerate() {
*value = try!(self.statement.read(i));
}
values
},
_ => {
let count = self.statement.columns();
let mut values = Vec::with_capacity(count);
for i in 0..count {
values.push(try!(self.statement.read(i)));
}
values
},
};
self.state = Some(try!(self.statement.step()));
self.values = Some(values);
Ok(Some(self.values.as_ref().unwrap()))
}
}
#[inline]
pub fn new<'l>(statement: Statement<'l>) -> Result<Iterator<'l>> {
Ok(Iterator { state: None, values: None, statement: statement })
}

View File

@ -163,9 +163,11 @@ impl error::Error for Error {
}
mod connection;
mod iterator;
mod statement;
pub use connection::Connection;
pub use iterator::Iterator;
pub use statement::{Statement, State, Bindable, Readable};
/// Open a connection to a new or existing database.

View File

@ -172,6 +172,12 @@ impl Bindable for () {
}
}
impl<'l, T: Bindable> Bindable for &'l T {
fn bind(&self, statement: &mut Statement, i: usize) -> Result<()> {
(*self).bind(statement, i)
}
}
impl Readable for Value {
fn read(statement: &Statement, i: usize) -> Result<Self> {
Ok(match statement.kind(i) {
@ -230,10 +236,10 @@ impl Readable for Vec<u8> {
}
#[inline]
pub fn new<'l, T: AsRef<str>>(raw1: *mut ffi::sqlite3, query: T) -> Result<Statement<'l>> {
pub fn new<'l, T: AsRef<str>>(raw1: *mut ffi::sqlite3, statement: T) -> Result<Statement<'l>> {
let mut raw0 = 0 as *mut _;
unsafe {
ok!(raw1, ffi::sqlite3_prepare_v2(raw1, str_to_cstr!(query.as_ref()).as_ptr(), -1,
ok!(raw1, ffi::sqlite3_prepare_v2(raw1, str_to_cstr!(statement.as_ref()).as_ptr(), -1,
&mut raw0, 0 as *mut _));
}
Ok(Statement { raw: (raw0, raw1), phantom: PhantomData })

View File

@ -1,7 +1,7 @@
extern crate sqlite;
extern crate temporary;
use sqlite::{Connection, State, Type};
use sqlite::{Connection, State, Type, Value};
use std::path::Path;
macro_rules! ok(
@ -26,8 +26,8 @@ fn connection_process() {
let connection = setup(":memory:");
let mut done = false;
let query = "SELECT * FROM users";
ok!(connection.process(query, |pairs| {
let statement = "SELECT * FROM users";
ok!(connection.process(statement, |pairs| {
assert_eq!(pairs.len(), 4);
assert_eq!(pairs[0], pair!("id", "1"));
assert_eq!(pairs[1], pair!("name", "Alice"));
@ -53,8 +53,8 @@ fn connection_set_busy_handler() {
thread::spawn(move || {
let mut connection = ok!(sqlite::open(&path));
ok!(connection.set_busy_handler(|_| true));
let query = "INSERT INTO `users` (id, name, age, photo) VALUES (?, ?, ?, ?)";
let mut statement = ok!(connection.prepare(query));
let statement = "INSERT INTO `users` (id, name, age, photo) VALUES (?, ?, ?, ?)";
let mut statement = ok!(connection.prepare(statement));
ok!(statement.bind(1, 2i64));
ok!(statement.bind(2, "Bob"));
ok!(statement.bind(3, 69.42));
@ -69,11 +69,25 @@ fn connection_set_busy_handler() {
}
}
#[test]
fn iterator() {
let connection = setup(":memory:");
let statement = "SELECT id FROM users WHERE id = ?";
let mut iterator = ok!(connection.iterate(statement));
ok!(iterator.start(&[Value::Integer(1)]));
assert_eq!(ok!(ok!(iterator.next())), &[Value::Integer(1)]);
assert_eq!(ok!(iterator.next()), None);
ok!(iterator.start(&[Value::Integer(42)]));
assert_eq!(ok!(iterator.next()), None);
}
#[test]
fn statement_columns() {
let connection = setup(":memory:");
let query = "SELECT * FROM users";
let mut statement = ok!(connection.prepare(query));
let statement = "SELECT * FROM users";
let mut statement = ok!(connection.prepare(statement));
assert_eq!(statement.columns(), 4);
@ -85,8 +99,8 @@ fn statement_columns() {
#[test]
fn statement_kind() {
let connection = setup(":memory:");
let query = "SELECT * FROM users";
let mut statement = ok!(connection.prepare(query));
let statement = "SELECT * FROM users";
let mut statement = ok!(connection.prepare(statement));
assert_eq!(statement.kind(0), Type::Null);
assert_eq!(statement.kind(1), Type::Null);
@ -104,8 +118,8 @@ fn statement_kind() {
#[test]
fn statement_bind() {
let connection = setup(":memory:");
let query = "INSERT INTO users (id, name, age, photo) VALUES (?, ?, ?, ?)";
let mut statement = ok!(connection.prepare(query));
let statement = "INSERT INTO users (id, name, age, photo) VALUES (?, ?, ?, ?)";
let mut statement = ok!(connection.prepare(statement));
ok!(statement.bind(1, 2i64));
ok!(statement.bind(2, "Bob"));
@ -117,8 +131,8 @@ fn statement_bind() {
#[test]
fn statement_read() {
let connection = setup(":memory:");
let query = "SELECT * FROM users";
let mut statement = ok!(connection.prepare(query));
let statement = "SELECT * FROM users";
let mut statement = ok!(connection.prepare(statement));
assert_eq!(ok!(statement.step()), State::Row);
assert_eq!(ok!(statement.read::<i64>(0)), 1);