#![crate_name = "sqlite3"]
#![crate_type = "lib"]
#![warn(missing_docs)]
extern crate libc;
extern crate time;
#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate enum_primitive;
use std::error::{Error};
use std::fmt::Display;
use std::fmt;
pub use core::Access;
pub use core::{DatabaseConnection, PreparedStatement, ResultSet, ResultRow};
pub use core::{ColIx, ParamIx};
pub use types::{FromSql, ToSql};
use self::SqliteErrorCode::SQLITE_MISUSE;
pub mod core;
pub mod types;
#[allow(non_camel_case_types, non_snake_case)]
#[allow(dead_code)]
#[allow(missing_docs)]
#[allow(missing_copy_implementations)] pub mod ffi;
pub mod access;
pub trait StatementUpdate {
fn update(&mut self,
values: &[&ToSql]) -> SqliteResult<u64>;
}
impl StatementUpdate for core::PreparedStatement {
fn update(&mut self,
values: &[&ToSql]) -> SqliteResult<u64> {
let check = {
try!(bind_values(self, values));
let mut results = self.execute();
match try!(results.step()) {
None => Ok(()),
Some(_row) => Err(SqliteError {
kind: SQLITE_MISUSE,
desc: "unexpected SQLITE_ROW from update",
detail: None
})
}
};
check.map(|_ok| self.changes())
}
}
pub trait QueryEach<F>
where F: FnMut(&mut ResultRow) -> SqliteResult<()>
{
fn query_each(&mut self,
values: &[&ToSql],
each_row: &mut F
) -> SqliteResult<()>;
}
impl<F> QueryEach<F> for core::PreparedStatement
where F: FnMut(&mut ResultRow) -> SqliteResult<()>
{
fn query_each(&mut self,
values: &[&ToSql],
each_row: &mut F
) -> SqliteResult<()>
{
try!(bind_values(self, values));
let mut results = self.execute();
loop {
match try!(results.step()) {
None => break,
Some(ref mut row) => try!(each_row(row)),
}
}
Ok(())
}
}
pub trait QueryFold<F, A>
where F: Fn(&mut ResultRow, A) -> SqliteResult<A>
{
fn query_fold(&mut self,
values: &[&ToSql],
init: A,
each_row: F
) -> SqliteResult<A>;
}
impl<F, A> QueryFold<F, A> for core::PreparedStatement
where F: Fn(&mut ResultRow, A) -> SqliteResult<A>
{
fn query_fold(&mut self,
values: &[&ToSql],
init: A,
f: F
) -> SqliteResult<A>
{
try!(bind_values(self, values));
let mut results = self.execute();
let mut accum = init;
loop {
match try!(results.step()) {
None => break,
Some(ref mut row) => accum = try!(f(row, accum)),
}
}
Ok(accum)
}
}
pub trait Query<F, T>
where F: FnMut(&mut ResultRow) -> SqliteResult<T>
{
fn query<'stmt>(&'stmt mut self,
values: &[&ToSql],
txform: F
) -> SqliteResult<QueryResults<'stmt, T, F>>;
}
impl<F, T> Query<F, T> for core::PreparedStatement
where F: FnMut(&mut ResultRow) -> SqliteResult<T>
{
fn query<'stmt>(&'stmt mut self,
values: &[&ToSql],
txform: F
) -> SqliteResult<QueryResults<'stmt, T, F>>
{
try!(bind_values(self, values));
let results = self.execute();
Ok(QueryResults { results: results, txform: txform })
}
}
pub struct QueryResults<'stmt, T, F>
where F: FnMut(&mut ResultRow) -> SqliteResult<T>
{
results: core::ResultSet<'stmt>,
txform: F
}
impl<'stmt, T, F> Iterator for QueryResults<'stmt, T, F>
where F: FnMut(&mut ResultRow) -> SqliteResult<T>
{
type Item = SqliteResult<T>;
fn next(&mut self) -> Option<SqliteResult<T>> {
match self.results.step() {
Ok(None) => None,
Ok(Some(ref mut row)) => Some((self.txform)(row)),
Err(e) => Some(Err(e))
}
}
}
fn bind_values(s: &mut PreparedStatement, values: &[&ToSql]) -> SqliteResult<()> {
for (ix, v) in values.iter().enumerate() {
let p = ix as ParamIx + 1;
try!(v.to_sql(s, p));
}
Ok(())
}
pub trait ResultRowAccess {
fn get<I: RowIndex + Display + Clone, T: FromSql>(&mut self, idx: I) -> T;
fn get_opt<I: RowIndex + Display + Clone, T: FromSql>(&mut self, idx: I) -> SqliteResult<T>;
}
impl<'res, 'row> ResultRowAccess for core::ResultRow<'res, 'row> {
fn get<I: RowIndex + Display + Clone, T: FromSql>(&mut self, idx: I) -> T {
match self.get_opt(idx.clone()) {
Ok(ok) => ok,
Err(err) => panic!("retrieving column {}: {}", idx, err)
}
}
fn get_opt<I: RowIndex + Display + Clone, T: FromSql>(&mut self, idx: I) -> SqliteResult<T> {
match idx.idx(self) {
Some(idx) => FromSql::from_sql(self, idx),
None => Err(SqliteError {
kind: SQLITE_MISUSE,
desc: "no such row name/number",
detail: Some(format!("{}", idx))
})
}
}
}
pub trait RowIndex {
fn idx(&self, row: &mut ResultRow) -> Option<ColIx>;
}
impl RowIndex for ColIx {
fn idx(&self, _row: &mut ResultRow) -> Option<ColIx> { Some(*self) }
}
impl RowIndex for &'static str {
fn idx(&self, row: &mut ResultRow) -> Option<ColIx> {
let mut ixs = 0 .. row.column_count();
ixs.find(|ix| row.with_column_name(*ix, false, |name| name == *self))
}
}
#[must_use]
pub type SqliteResult<T> = Result<T, SqliteError>;
enum_from_primitive! {
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[allow(non_camel_case_types)]
#[allow(missing_docs)]
pub enum SqliteErrorCode {
SQLITE_ERROR = 1,
SQLITE_INTERNAL = 2,
SQLITE_PERM = 3,
SQLITE_ABORT = 4,
SQLITE_BUSY = 5,
SQLITE_LOCKED = 6,
SQLITE_NOMEM = 7,
SQLITE_READONLY = 8,
SQLITE_INTERRUPT = 9,
SQLITE_IOERR = 10,
SQLITE_CORRUPT = 11,
SQLITE_NOTFOUND = 12,
SQLITE_FULL = 13,
SQLITE_CANTOPEN = 14,
SQLITE_PROTOCOL = 15,
SQLITE_EMPTY = 16,
SQLITE_SCHEMA = 17,
SQLITE_TOOBIG = 18,
SQLITE_CONSTRAINT= 19,
SQLITE_MISMATCH = 20,
SQLITE_MISUSE = 21,
SQLITE_NOLFS = 22,
SQLITE_AUTH = 23,
SQLITE_FORMAT = 24,
SQLITE_RANGE = 25,
SQLITE_NOTADB = 26
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct SqliteError {
pub kind: SqliteErrorCode,
pub desc: &'static str,
pub detail: Option<String>
}
impl Display for SqliteError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.detail {
Some(ref x) => f.write_fmt(format_args!("{} ({})", x, self.kind as u32)),
None => f.write_fmt(format_args!("{} ({})", self.desc, self.kind as u32))
}
}
}
impl SqliteError {
pub fn detail(&self) -> Option<String> { self.detail.clone() }
}
impl Error for SqliteError {
fn description(&self) -> &str { self.desc }
fn cause(&self) -> Option<&Error> { None }
}
enum_from_primitive! {
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[allow(non_camel_case_types)]
#[allow(missing_docs)]
pub enum ColumnType {
SQLITE_INTEGER = 1,
SQLITE_FLOAT = 2,
SQLITE_TEXT = 3,
SQLITE_BLOB = 4,
SQLITE_NULL = 5
}
}
#[cfg(test)]
mod bind_tests {
use super::{DatabaseConnection, ResultSet};
use super::{ResultRowAccess};
use super::{SqliteResult};
#[test]
fn bind_fun() {
fn go() -> SqliteResult<()> {
let mut database = try!(DatabaseConnection::in_memory());
try!(database.exec(
"BEGIN;
CREATE TABLE test (id int, name text, address text);
INSERT INTO test (id, name, address) VALUES (1, 'John Doe', '123 w Pine');
COMMIT;"));
{
let mut tx = try!(database.prepare(
"INSERT INTO test (id, name, address) VALUES (?, ?, ?)"));
assert_eq!(tx.bind_parameter_count(), 3);
try!(tx.bind_int(1, 2));
try!(tx.bind_text(2, "Jane Doe"));
try!(tx.bind_text(3, "345 e Walnut"));
let mut results = tx.execute();
assert!(results.step().ok().unwrap().is_none());
}
assert_eq!(database.changes(), 1);
let mut q = try!(database.prepare("select * from test order by id"));
let mut rows = q.execute();
match rows.step() {
Ok(Some(ref mut row)) => {
assert_eq!(row.get::<u32, i32>(0), 1);
},
_ => panic!()
}
match rows.step() {
Ok(Some(ref mut row)) => {
assert_eq!(row.get::<u32, i32>(0), 2);
},
_ => panic!()
}
Ok(())
}
match go() {
Ok(_) => (),
Err(e) => panic!("oops! {:?}", e)
}
}
fn with_query<T, F>(sql: &str, mut f: F) -> SqliteResult<T>
where F: FnMut(&mut ResultSet) -> T
{
let db = try!(DatabaseConnection::in_memory());
let mut s = try!(db.prepare(sql));
let mut rows = s.execute();
let x = f(&mut rows);
return Ok(x);
}
#[test]
fn named_rowindex() {
fn go() -> SqliteResult<(u32, i32)> {
let mut count = 0;
let mut sum = 0i32;
with_query("select 1 as col1
union all
select 2", |rows| {
loop {
match rows.step() {
Ok(Some(ref mut row)) => {
count += 1;
sum += row.column_int(0);
},
_ => break
}
}
(count, sum)
})
}
assert_eq!(go(), Ok((2, 3)))
}
#[test]
fn err_with_detail() {
let io = || {
let mut conn = try!(DatabaseConnection::in_memory());
conn.exec("CREATE gobbledygook")
};
let go = || match io() {
Ok(_) => panic!(),
Err(oops) => {
format!("{:?}: {}: {}",
oops.kind, oops.desc,
oops.detail.unwrap())
}
};
let expected = "SQLITE_ERROR: sqlite3_exec: near \"gobbledygook\": syntax error";
assert_eq!(go(), expected.to_string())
}
}