mirror of
https://github.com/fluencelabs/sqlite-wasm-connector
synced 2025-03-15 06:20:50 +00:00
Introduce Iterator
This commit is contained in:
parent
c4772f7dd8
commit
d89cc13d7b
@ -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"
|
||||
|
@ -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
57
src/iterator.rs
Normal 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 })
|
||||
}
|
@ -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.
|
||||
|
@ -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 })
|
||||
|
40
tests/lib.rs
40
tests/lib.rs
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user