sqlite-wasm-connector/src/statement.rs

262 lines
7.9 KiB
Rust
Raw Normal View History

2015-06-12 14:23:18 -04:00
use ffi;
2015-05-29 11:24:01 -04:00
use libc::{c_double, c_int};
use std::marker::PhantomData;
2015-08-03 17:10:30 -04:00
use {Cursor, Result, Type, Value};
2015-05-29 11:24:01 -04:00
/// A prepared statement.
pub struct Statement<'l> {
state: Option<State>,
2015-06-12 14:23:18 -04:00
raw: (*mut ffi::sqlite3_stmt, *mut ffi::sqlite3),
phantom: PhantomData<(ffi::sqlite3_stmt, &'l ffi::sqlite3)>,
2015-05-29 11:24:01 -04:00
}
2015-06-19 11:34:03 -04:00
/// A state of a prepared statement.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum State {
2015-06-19 13:03:54 -04:00
/// There is a row available for reading.
2015-06-19 11:34:03 -04:00
Row,
/// The statement has been entirely evaluated.
2015-06-19 13:03:54 -04:00
Done,
2015-06-19 11:34:03 -04:00
}
/// A type suitable for binding to a prepared statement.
pub trait Bindable {
/// Bind to a parameter.
2015-06-19 11:31:29 -04:00
///
/// The leftmost parameter has the index 1.
2015-06-19 13:10:09 -04:00
fn bind(&self, &mut Statement, usize) -> Result<()>;
2015-05-29 11:24:01 -04:00
}
/// A type suitable for reading from a prepared statement.
pub trait Readable {
/// Read from a column.
2015-06-19 11:31:29 -04:00
///
/// The leftmost column has the index 0.
fn read(&Statement, usize) -> Result<Self>;
2015-05-29 13:08:02 -04:00
}
2015-05-29 11:24:01 -04:00
impl<'l> Statement<'l> {
/// Bind a value to a parameter.
///
/// The leftmost parameter has the index 1.
#[inline]
pub fn bind<T: Bindable>(&mut self, i: usize, parameter: T) -> Result<()> {
parameter.bind(self, i)
}
2015-08-01 13:55:34 -04:00
/// Return the number of columns.
#[inline]
pub fn columns(&self) -> usize {
2015-08-01 13:55:34 -04:00
unsafe { ffi::sqlite3_column_count(self.raw.0) as usize }
}
/// Advance to the next state.
///
/// The function should be called multiple times until `State::Done` is
/// reached in order to evaluate the statement entirely.
pub fn next(&mut self) -> Result<State> {
let state = match unsafe { ffi::sqlite3_step(self.raw.0) } {
ffi::SQLITE_ROW => State::Row,
ffi::SQLITE_DONE => State::Done,
code => error!(self.raw.1, code),
};
self.state = Some(state);
Ok(state)
}
/// Return the current state.
#[inline]
pub fn state(&self) -> Option<State> {
self.state
}
/// Return the type of a column.
///
/// The type is revealed after the first step has been taken.
pub fn kind(&self, i: usize) -> Type {
match unsafe { ffi::sqlite3_column_type(self.raw.0, i as c_int) } {
2015-08-01 17:51:43 -04:00
ffi::SQLITE_BLOB => Type::Binary,
ffi::SQLITE_FLOAT => Type::Float,
ffi::SQLITE_INTEGER => Type::Integer,
ffi::SQLITE_TEXT => Type::String,
ffi::SQLITE_NULL => Type::Null,
_ => unreachable!(),
}
}
/// Read a value from a column.
///
/// The leftmost column has the index 0.
2015-05-29 13:08:02 -04:00
#[inline]
pub fn read<T: Readable>(&self, i: usize) -> Result<T> {
Readable::read(self, i)
2015-05-29 13:08:02 -04:00
}
/// Reset the statement.
2015-05-29 11:24:01 -04:00
#[inline]
pub fn reset(&mut self) -> Result<()> {
2015-06-19 20:30:28 -04:00
unsafe { ok!(self.raw.1, ffi::sqlite3_reset(self.raw.0)) };
self.state = None;
2015-05-29 11:24:01 -04:00
Ok(())
}
2015-08-03 17:10:30 -04:00
/// Upgrade to a cursor.
#[inline]
2015-08-03 17:10:30 -04:00
pub fn cursor(self) -> Result<Cursor<'l>> {
::cursor::new(self)
}
2015-05-29 11:24:01 -04:00
}
impl<'l> Drop for Statement<'l> {
#[inline]
fn drop(&mut self) {
2015-06-12 14:23:18 -04:00
unsafe { ffi::sqlite3_finalize(self.raw.0) };
2015-05-29 11:24:01 -04:00
}
}
impl Bindable for Value {
fn bind(&self, statement: &mut Statement, i: usize) -> Result<()> {
match self {
&Value::Binary(ref value) => (value as &[u8]).bind(statement, i),
&Value::Float(value) => value.bind(statement, i),
&Value::Integer(value) => value.bind(statement, i),
&Value::String(ref value) => (value as &str).bind(statement, i),
&Value::Null => ().bind(statement, i),
}
}
}
impl<'l> Bindable for &'l [u8] {
#[inline]
fn bind(&self, statement: &mut Statement, i: usize) -> Result<()> {
debug_assert!(i > 0, "the indexing starts from 1");
unsafe {
ok!(statement.raw.1, ffi::sqlite3_bind_blob(statement.raw.0, i as c_int,
self.as_ptr() as *const _,
self.len() as c_int, None));
}
Ok(())
}
}
impl Bindable for f64 {
2015-06-19 11:31:29 -04:00
#[inline]
2015-06-19 13:10:09 -04:00
fn bind(&self, statement: &mut Statement, i: usize) -> Result<()> {
2015-06-19 11:31:29 -04:00
debug_assert!(i > 0, "the indexing starts from 1");
unsafe {
2015-06-19 20:30:28 -04:00
ok!(statement.raw.1, ffi::sqlite3_bind_double(statement.raw.0, i as c_int,
*self as c_double));
2015-06-19 11:31:29 -04:00
}
Ok(())
}
}
impl Bindable for i64 {
2015-06-19 11:31:29 -04:00
#[inline]
2015-06-19 13:10:09 -04:00
fn bind(&self, statement: &mut Statement, i: usize) -> Result<()> {
2015-06-19 11:31:29 -04:00
debug_assert!(i > 0, "the indexing starts from 1");
unsafe {
2015-06-19 20:30:28 -04:00
ok!(statement.raw.1, ffi::sqlite3_bind_int64(statement.raw.0, i as c_int,
*self as ffi::sqlite3_int64));
2015-06-19 11:31:29 -04:00
}
Ok(())
}
}
2015-08-02 20:37:17 -04:00
impl<'l> Bindable for &'l str {
2015-06-19 11:31:29 -04:00
#[inline]
2015-06-19 13:10:09 -04:00
fn bind(&self, statement: &mut Statement, i: usize) -> Result<()> {
2015-06-19 11:31:29 -04:00
debug_assert!(i > 0, "the indexing starts from 1");
unsafe {
2015-08-02 20:37:17 -04:00
ok!(statement.raw.1, ffi::sqlite3_bind_text(statement.raw.0, i as c_int,
str_to_cstr!(*self).as_ptr(), -1, None));
2015-06-19 11:31:29 -04:00
}
Ok(())
}
}
2015-08-02 20:37:17 -04:00
impl Bindable for () {
#[inline]
fn bind(&self, statement: &mut Statement, i: usize) -> Result<()> {
debug_assert!(i > 0, "the indexing starts from 1");
unsafe {
2015-08-02 20:37:17 -04:00
ok!(statement.raw.1, ffi::sqlite3_bind_null(statement.raw.0, i as c_int));
}
Ok(())
}
}
2015-08-02 22:29:04 -04:00
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) {
Type::Binary => Value::Binary(try!(Readable::read(statement, i))),
Type::Float => Value::Float(try!(Readable::read(statement, i))),
Type::Integer => Value::Integer(try!(Readable::read(statement, i))),
2015-08-03 16:50:21 -04:00
Type::String => Value::String(try!(Readable::read(statement, i))),
Type::Null => Value::Null,
})
}
}
impl Readable for f64 {
2015-06-19 11:31:29 -04:00
#[inline]
fn read(statement: &Statement, i: usize) -> Result<Self> {
2015-06-12 14:23:18 -04:00
Ok(unsafe { ffi::sqlite3_column_double(statement.raw.0, i as c_int) as f64 })
2015-05-29 13:08:02 -04:00
}
}
impl Readable for i64 {
2015-06-19 11:31:29 -04:00
#[inline]
fn read(statement: &Statement, i: usize) -> Result<Self> {
2015-06-12 14:23:18 -04:00
Ok(unsafe { ffi::sqlite3_column_int64(statement.raw.0, i as c_int) as i64 })
2015-05-29 13:08:02 -04:00
}
}
impl Readable for String {
2015-06-19 11:31:29 -04:00
#[inline]
fn read(statement: &Statement, i: usize) -> Result<Self> {
2015-05-29 13:08:02 -04:00
unsafe {
2015-06-12 14:23:18 -04:00
let pointer = ffi::sqlite3_column_text(statement.raw.0, i as c_int);
2015-05-29 13:08:02 -04:00
if pointer.is_null() {
2015-06-19 20:24:34 -04:00
raise!("cannot read a text column");
2015-05-29 13:08:02 -04:00
}
Ok(c_str_to_string!(pointer))
}
}
}
impl Readable for Vec<u8> {
#[inline]
fn read(statement: &Statement, i: usize) -> Result<Self> {
use std::ptr::copy_nonoverlapping as copy;
unsafe {
let pointer = ffi::sqlite3_column_blob(statement.raw.0, i as c_int);
if pointer.is_null() {
return Ok(vec![]);
}
let count = ffi::sqlite3_column_bytes(statement.raw.0, i as c_int) as usize;
let mut buffer = Vec::with_capacity(count);
buffer.set_len(count);
copy(pointer as *const u8, buffer.as_mut_ptr(), count);
Ok(buffer)
}
}
}
2015-05-29 11:24:01 -04:00
#[inline]
2015-08-02 22:29:04 -04:00
pub fn new<'l, T: AsRef<str>>(raw1: *mut ffi::sqlite3, statement: T) -> Result<Statement<'l>> {
2015-06-08 17:43:31 -04:00
let mut raw0 = 0 as *mut _;
unsafe {
2015-08-02 22:29:04 -04:00
ok!(raw1, ffi::sqlite3_prepare_v2(raw1, str_to_cstr!(statement.as_ref()).as_ptr(), -1,
2015-07-31 16:05:52 -04:00
&mut raw0, 0 as *mut _));
}
Ok(Statement { state: None, raw: (raw0, raw1), phantom: PhantomData })
2015-05-29 11:24:01 -04:00
}