mirror of
https://github.com/fluencelabs/sqlite-wasm-connector
synced 2025-04-21 23:22:14 +00:00
Introduce Iterator
This commit is contained in:
parent
c4772f7dd8
commit
d89cc13d7b
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "sqlite"
|
name = "sqlite"
|
||||||
version = "0.17.2"
|
version = "0.18.0"
|
||||||
authors = ["Ivan Ukhov <ivan.ukhov@gmail.com>"]
|
authors = ["Ivan Ukhov <ivan.ukhov@gmail.com>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/stainless-steel/sqlite"
|
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::marker::PhantomData;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use {Result, Statement};
|
use {Iterator, Result, Statement};
|
||||||
|
|
||||||
/// A connection to a database.
|
/// A database connection.
|
||||||
pub struct Connection {
|
pub struct Connection {
|
||||||
raw: *mut ffi::sqlite3,
|
raw: *mut ffi::sqlite3,
|
||||||
busy_callback: Option<Box<FnMut(usize) -> bool>>,
|
busy_callback: Option<Box<FnMut(usize) -> bool>>,
|
||||||
@ -24,28 +24,29 @@ impl Connection {
|
|||||||
Ok(Connection { raw: raw, busy_callback: None, phantom: PhantomData })
|
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]
|
#[inline]
|
||||||
pub fn execute<T: AsRef<str>>(&self, query: T) -> Result<()> {
|
pub fn execute<T: AsRef<str>>(&self, statement: T) -> Result<()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
ok!(self.raw, ffi::sqlite3_exec(self.raw, str_to_cstr!(query.as_ref()).as_ptr(), None,
|
ok!(self.raw, ffi::sqlite3_exec(self.raw, str_to_cstr!(statement.as_ref()).as_ptr(),
|
||||||
0 as *mut _, 0 as *mut _));
|
None, 0 as *mut _, 0 as *mut _));
|
||||||
}
|
}
|
||||||
Ok(())
|
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`,
|
/// 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
|
/// 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]
|
#[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
|
where F: FnMut(&[(&str, Option<&str>)]) -> bool
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
let callback = Box::new(callback);
|
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>),
|
Some(process_callback::<F>),
|
||||||
&*callback as *const F as *mut F as *mut _,
|
&*callback as *const F as *mut F as *mut _,
|
||||||
0 as *mut _));
|
0 as *mut _));
|
||||||
@ -55,8 +56,15 @@ impl Connection {
|
|||||||
|
|
||||||
/// Create a prepared statement.
|
/// Create a prepared statement.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn prepare<'l, T: AsRef<str>>(&'l self, query: T) -> Result<Statement<'l>> {
|
pub fn prepare<'l, T: AsRef<str>>(&'l self, statement: T) -> Result<Statement<'l>> {
|
||||||
::statement::new(self.raw, query)
|
::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.
|
/// 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 connection;
|
||||||
|
mod iterator;
|
||||||
mod statement;
|
mod statement;
|
||||||
|
|
||||||
pub use connection::Connection;
|
pub use connection::Connection;
|
||||||
|
pub use iterator::Iterator;
|
||||||
pub use statement::{Statement, State, Bindable, Readable};
|
pub use statement::{Statement, State, Bindable, Readable};
|
||||||
|
|
||||||
/// Open a connection to a new or existing database.
|
/// 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 {
|
impl Readable for Value {
|
||||||
fn read(statement: &Statement, i: usize) -> Result<Self> {
|
fn read(statement: &Statement, i: usize) -> Result<Self> {
|
||||||
Ok(match statement.kind(i) {
|
Ok(match statement.kind(i) {
|
||||||
@ -230,10 +236,10 @@ impl Readable for Vec<u8> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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 _;
|
let mut raw0 = 0 as *mut _;
|
||||||
unsafe {
|
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 _));
|
&mut raw0, 0 as *mut _));
|
||||||
}
|
}
|
||||||
Ok(Statement { raw: (raw0, raw1), phantom: PhantomData })
|
Ok(Statement { raw: (raw0, raw1), phantom: PhantomData })
|
||||||
|
40
tests/lib.rs
40
tests/lib.rs
@ -1,7 +1,7 @@
|
|||||||
extern crate sqlite;
|
extern crate sqlite;
|
||||||
extern crate temporary;
|
extern crate temporary;
|
||||||
|
|
||||||
use sqlite::{Connection, State, Type};
|
use sqlite::{Connection, State, Type, Value};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
macro_rules! ok(
|
macro_rules! ok(
|
||||||
@ -26,8 +26,8 @@ fn connection_process() {
|
|||||||
let connection = setup(":memory:");
|
let connection = setup(":memory:");
|
||||||
|
|
||||||
let mut done = false;
|
let mut done = false;
|
||||||
let query = "SELECT * FROM users";
|
let statement = "SELECT * FROM users";
|
||||||
ok!(connection.process(query, |pairs| {
|
ok!(connection.process(statement, |pairs| {
|
||||||
assert_eq!(pairs.len(), 4);
|
assert_eq!(pairs.len(), 4);
|
||||||
assert_eq!(pairs[0], pair!("id", "1"));
|
assert_eq!(pairs[0], pair!("id", "1"));
|
||||||
assert_eq!(pairs[1], pair!("name", "Alice"));
|
assert_eq!(pairs[1], pair!("name", "Alice"));
|
||||||
@ -53,8 +53,8 @@ fn connection_set_busy_handler() {
|
|||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let mut connection = ok!(sqlite::open(&path));
|
let mut connection = ok!(sqlite::open(&path));
|
||||||
ok!(connection.set_busy_handler(|_| true));
|
ok!(connection.set_busy_handler(|_| true));
|
||||||
let query = "INSERT INTO `users` (id, name, age, photo) VALUES (?, ?, ?, ?)";
|
let statement = "INSERT INTO `users` (id, name, age, photo) VALUES (?, ?, ?, ?)";
|
||||||
let mut statement = ok!(connection.prepare(query));
|
let mut statement = ok!(connection.prepare(statement));
|
||||||
ok!(statement.bind(1, 2i64));
|
ok!(statement.bind(1, 2i64));
|
||||||
ok!(statement.bind(2, "Bob"));
|
ok!(statement.bind(2, "Bob"));
|
||||||
ok!(statement.bind(3, 69.42));
|
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]
|
#[test]
|
||||||
fn statement_columns() {
|
fn statement_columns() {
|
||||||
let connection = setup(":memory:");
|
let connection = setup(":memory:");
|
||||||
let query = "SELECT * FROM users";
|
let statement = "SELECT * FROM users";
|
||||||
let mut statement = ok!(connection.prepare(query));
|
let mut statement = ok!(connection.prepare(statement));
|
||||||
|
|
||||||
assert_eq!(statement.columns(), 4);
|
assert_eq!(statement.columns(), 4);
|
||||||
|
|
||||||
@ -85,8 +99,8 @@ fn statement_columns() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn statement_kind() {
|
fn statement_kind() {
|
||||||
let connection = setup(":memory:");
|
let connection = setup(":memory:");
|
||||||
let query = "SELECT * FROM users";
|
let statement = "SELECT * FROM users";
|
||||||
let mut statement = ok!(connection.prepare(query));
|
let mut statement = ok!(connection.prepare(statement));
|
||||||
|
|
||||||
assert_eq!(statement.kind(0), Type::Null);
|
assert_eq!(statement.kind(0), Type::Null);
|
||||||
assert_eq!(statement.kind(1), Type::Null);
|
assert_eq!(statement.kind(1), Type::Null);
|
||||||
@ -104,8 +118,8 @@ fn statement_kind() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn statement_bind() {
|
fn statement_bind() {
|
||||||
let connection = setup(":memory:");
|
let connection = setup(":memory:");
|
||||||
let query = "INSERT INTO users (id, name, age, photo) VALUES (?, ?, ?, ?)";
|
let statement = "INSERT INTO users (id, name, age, photo) VALUES (?, ?, ?, ?)";
|
||||||
let mut statement = ok!(connection.prepare(query));
|
let mut statement = ok!(connection.prepare(statement));
|
||||||
|
|
||||||
ok!(statement.bind(1, 2i64));
|
ok!(statement.bind(1, 2i64));
|
||||||
ok!(statement.bind(2, "Bob"));
|
ok!(statement.bind(2, "Bob"));
|
||||||
@ -117,8 +131,8 @@ fn statement_bind() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn statement_read() {
|
fn statement_read() {
|
||||||
let connection = setup(":memory:");
|
let connection = setup(":memory:");
|
||||||
let query = "SELECT * FROM users";
|
let statement = "SELECT * FROM users";
|
||||||
let mut statement = ok!(connection.prepare(query));
|
let mut statement = ok!(connection.prepare(statement));
|
||||||
|
|
||||||
assert_eq!(ok!(statement.step()), State::Row);
|
assert_eq!(ok!(statement.step()), State::Row);
|
||||||
assert_eq!(ok!(statement.read::<i64>(0)), 1);
|
assert_eq!(ok!(statement.read::<i64>(0)), 1);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user