use ffi;
use libc::{c_double, c_int};
use std::collections::HashMap;
use std::marker::PhantomData;
use std::rc::Rc;
use cursor::{Cursor, CursorWithOwnership, Row};
use error::Result;
use value::{Type, Value};
macro_rules! transient(
() => (::std::mem::transmute(!0 as *const ::libc::c_void));
);
#[derive(Debug, Clone)]
pub struct Statement {
raw: *mut ffi::sqlite3_stmt,
column_names: Vec<String>,
column_mapping: Rc<HashMap<String, usize>>,
phantom: PhantomData<ffi::sqlite3_stmt>,
}
unsafe impl<'l> Sync for Statement {}
unsafe impl<'l> Send for Statement {}
pub trait Bindable {
fn bind(self, _: &mut Statement) -> Result<()>;
}
pub trait BindableWithIndex {
fn bind<T: ParameterIndex>(self, _: &mut Statement, _: T) -> Result<()>;
}
pub trait ColumnIndex: Copy + std::fmt::Debug {
fn index(self, statement: &Statement) -> Result<usize>;
}
pub trait ParameterIndex: Copy + std::fmt::Debug {
fn index(self, statement: &Statement) -> Result<usize>;
}
pub trait ReadableWithIndex: Sized {
fn read<T: ColumnIndex>(_: &Statement, _: T) -> Result<Self>;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum State {
Row,
Done,
}
impl Statement {
#[inline]
pub fn bind<T: Bindable>(&mut self, value: T) -> Result<()> {
value.bind(self)?;
Ok(())
}
pub fn bind_iter<T, U>(&mut self, value: T) -> Result<()>
where
T: IntoIterator<Item = U>,
U: Bindable,
{
for value in value {
self.bind(value)?;
}
Ok(())
}
#[inline]
pub fn column_count(&self) -> usize {
self.column_names.len()
}
#[doc(hidden)]
#[inline]
pub fn column_mapping(&self) -> Rc<HashMap<String, usize>> {
self.column_mapping.clone()
}
#[inline]
pub fn column_name<T: ColumnIndex>(&self, index: T) -> Result<&str> {
Ok(&self.column_names[index.index(self)?])
}
#[inline]
pub fn column_names(&self) -> &[String] {
&self.column_names
}
pub fn column_type<T: ColumnIndex>(&self, index: T) -> Result<Type> {
Ok(
match unsafe { ffi::sqlite3_column_type(self.raw, index.index(self)? as c_int) } {
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!(),
},
)
}
#[inline]
pub fn iter(&mut self) -> Cursor {
self.into()
}
pub fn next(&mut self) -> Result<State> {
Ok(match unsafe { ffi::sqlite3_step(self.raw) } {
ffi::SQLITE_ROW => State::Row,
ffi::SQLITE_DONE => State::Done,
code => {
return Err(::Error {
code: Some(code as isize),
message: None,
})
}
})
}
#[inline]
pub fn parameter_index(&self, parameter: &str) -> Result<Option<usize>> {
let index = unsafe {
ffi::sqlite3_bind_parameter_index(self.raw, str_to_cstr!(parameter).as_ptr())
};
match index {
0 => Ok(None),
_ => Ok(Some(index as usize)),
}
}
#[inline]
pub fn read<T, U>(&self, index: U) -> Result<T>
where
T: ReadableWithIndex,
U: ColumnIndex,
{
ReadableWithIndex::read(self, index)
}
#[inline]
pub fn reset(&mut self) -> Result<()> {
unsafe { ok!(ffi::sqlite3_reset(self.raw)) };
Ok(())
}
#[doc(hidden)]
#[inline]
pub fn as_raw(&self) -> *mut ffi::sqlite3_stmt {
self.raw
}
}
impl Drop for Statement {
#[inline]
fn drop(&mut self) {
unsafe { ffi::sqlite3_finalize(self.raw) };
}
}
impl<'m> From<&'m mut Statement> for Cursor<'m> {
#[inline]
fn from(statement: &'m mut Statement) -> Self {
::cursor::new(statement)
}
}
impl IntoIterator for Statement {
type Item = Result<Row>;
type IntoIter = CursorWithOwnership;
#[inline]
fn into_iter(self) -> Self::IntoIter {
::cursor::new_with_ownership(self)
}
}
impl<T, U> Bindable for (T, U)
where
T: ParameterIndex,
U: BindableWithIndex,
{
#[inline]
fn bind(self, statement: &mut Statement) -> Result<()> {
self.1.bind(statement, self.0)
}
}
impl<T> Bindable for &[T]
where
T: BindableWithIndex + Clone,
{
fn bind(self, statement: &mut Statement) -> Result<()> {
for (index, value) in self.iter().enumerate() {
value.clone().bind(statement, index + 1)?;
}
Ok(())
}
}
impl<T, U> Bindable for &[(T, U)]
where
T: ParameterIndex,
U: BindableWithIndex + Clone,
{
fn bind(self, statement: &mut Statement) -> Result<()> {
for (index, value) in self.iter() {
value.clone().bind(statement, *index)?;
}
Ok(())
}
}
impl BindableWithIndex for &[u8] {
#[inline]
fn bind<T: ParameterIndex>(self, statement: &mut Statement, index: T) -> Result<()> {
unsafe {
ok!(ffi::sqlite3_bind_blob(
statement.raw,
index.index(statement)? as c_int,
self.as_ptr() as *const _,
self.len() as c_int,
transient!(),
));
}
Ok(())
}
}
impl BindableWithIndex for f64 {
#[inline]
fn bind<T: ParameterIndex>(self, statement: &mut Statement, index: T) -> Result<()> {
unsafe {
ok!(ffi::sqlite3_bind_double(
statement.raw,
index.index(statement)? as c_int,
self as c_double
));
}
Ok(())
}
}
impl BindableWithIndex for i64 {
#[inline]
fn bind<T: ParameterIndex>(self, statement: &mut Statement, index: T) -> Result<()> {
unsafe {
ok!(ffi::sqlite3_bind_int64(
statement.raw,
index.index(statement)? as c_int,
self as ffi::sqlite3_int64
));
}
Ok(())
}
}
impl BindableWithIndex for &str {
#[inline]
fn bind<T: ParameterIndex>(self, statement: &mut Statement, index: T) -> Result<()> {
unsafe {
let value = ffi::sqlite3_bind_text(
statement.raw,
index.index(statement)? as c_int,
self.as_ptr() as *const _,
self.len() as c_int,
transient!(),
);
ok!(value);
}
Ok(())
}
}
impl BindableWithIndex for () {
#[inline]
fn bind<T: ParameterIndex>(self, statement: &mut Statement, index: T) -> Result<()> {
unsafe {
ok!(ffi::sqlite3_bind_null(
statement.raw,
index.index(statement)? as c_int
));
}
Ok(())
}
}
impl BindableWithIndex for Value {
#[inline]
fn bind<T: ParameterIndex>(self, statement: &mut Statement, index: T) -> Result<()> {
(index, &self).bind(statement)
}
}
impl BindableWithIndex for &Value {
fn bind<T: ParameterIndex>(self, statement: &mut Statement, index: T) -> Result<()> {
match self {
&Value::Binary(ref value) => (value as &[u8]).bind(statement, index),
&Value::Float(value) => value.bind(statement, index),
&Value::Integer(value) => value.bind(statement, index),
&Value::String(ref value) => (value as &str).bind(statement, index),
&Value::Null => ().bind(statement, index),
}
}
}
impl<T> BindableWithIndex for Option<T>
where
T: BindableWithIndex,
{
#[inline]
fn bind<U: ParameterIndex>(self, statement: &mut Statement, index: U) -> Result<()> {
match self {
Some(value) => value.bind(statement, index),
None => ().bind(statement, index),
}
}
}
impl<T> BindableWithIndex for &Option<T>
where
T: BindableWithIndex + Clone,
{
#[inline]
fn bind<U: ParameterIndex>(self, statement: &mut Statement, index: U) -> Result<()> {
match self {
Some(value) => value.clone().bind(statement, index),
None => ().bind(statement, index),
}
}
}
impl ColumnIndex for &str {
#[inline]
fn index(self, statement: &Statement) -> Result<usize> {
if statement.column_mapping.contains_key(self) {
Ok(statement.column_mapping[self])
} else {
raise!("the index is out of range ({})", self);
}
}
}
impl ColumnIndex for usize {
#[inline]
fn index(self, statement: &Statement) -> Result<usize> {
if self < statement.column_count() {
Ok(self)
} else {
raise!("the index is out of range ({})", self);
}
}
}
impl ParameterIndex for &str {
#[inline]
fn index(self, statement: &Statement) -> Result<usize> {
match statement.parameter_index(self)? {
Some(index) => return Ok(index),
_ => raise!("the index is out of range ({})", self),
}
}
}
impl ParameterIndex for usize {
#[inline]
fn index(self, _: &Statement) -> Result<usize> {
if self > 0 {
Ok(self)
} else {
raise!("the index is out of range ({})", self);
}
}
}
impl ReadableWithIndex for Value {
fn read<T: ColumnIndex>(statement: &Statement, index: T) -> Result<Self> {
Ok(match statement.column_type(index)? {
Type::Binary => Value::Binary(ReadableWithIndex::read(statement, index)?),
Type::Float => Value::Float(ReadableWithIndex::read(statement, index)?),
Type::Integer => Value::Integer(ReadableWithIndex::read(statement, index)?),
Type::String => Value::String(ReadableWithIndex::read(statement, index)?),
Type::Null => Value::Null,
})
}
}
impl ReadableWithIndex for f64 {
#[inline]
fn read<T: ColumnIndex>(statement: &Statement, index: T) -> Result<Self> {
Ok(unsafe {
ffi::sqlite3_column_double(statement.raw, index.index(statement)? as c_int) as f64
})
}
}
impl ReadableWithIndex for i64 {
#[inline]
fn read<T: ColumnIndex>(statement: &Statement, index: T) -> Result<Self> {
Ok(unsafe {
ffi::sqlite3_column_int64(statement.raw, index.index(statement)? as c_int) as i64
})
}
}
impl ReadableWithIndex for String {
#[inline]
fn read<T: ColumnIndex>(statement: &Statement, index: T) -> Result<Self> {
unsafe {
let pointer = ffi::sqlite3_column_text(statement.raw, index.index(statement)? as c_int);
if pointer.is_null() {
raise!("cannot read a text column");
}
Ok(c_str_to_string!(pointer))
}
}
}
impl ReadableWithIndex for Vec<u8> {
#[inline]
fn read<T: ColumnIndex>(statement: &Statement, index: T) -> Result<Self> {
use std::ptr::copy_nonoverlapping as copy;
unsafe {
let pointer = ffi::sqlite3_column_blob(statement.raw, index.index(statement)? as c_int);
if pointer.is_null() {
return Ok(vec![]);
}
let count =
ffi::sqlite3_column_bytes(statement.raw, index.index(statement)? 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)
}
}
}
impl<T: ReadableWithIndex> ReadableWithIndex for Option<T> {
#[inline]
fn read<U: ColumnIndex>(statement: &Statement, index: U) -> Result<Self> {
if statement.column_type(index)? == Type::Null {
Ok(None)
} else {
T::read(statement, index).map(Some)
}
}
}
#[inline]
pub fn new<'l, T>(raw_connection: *mut ffi::sqlite3, statement: T) -> Result<Statement>
where
T: AsRef<str>,
{
let mut raw_statement = 0 as *mut _;
unsafe {
ok!(
raw_connection,
ffi::sqlite3_prepare_v2(
raw_connection,
str_to_cstr!(statement.as_ref()).as_ptr(),
-1,
&mut raw_statement,
0 as *mut _,
)
);
}
let column_count = unsafe { ffi::sqlite3_column_count(raw_statement) as usize };
let column_names = (0..column_count)
.map(|index| unsafe {
let raw = ffi::sqlite3_column_name(raw_statement, index as c_int);
debug_assert!(!raw.is_null());
c_str_to_str!(raw).unwrap().to_string()
})
.collect::<Vec<_>>();
let column_mapping = column_names
.iter()
.enumerate()
.map(|(index, name)| (name.to_string(), index))
.collect();
Ok(Statement {
raw: raw_statement,
column_names: column_names,
column_mapping: Rc::new(column_mapping),
phantom: PhantomData,
})
}