1#![crate_name = "sqlite3"]
83#![crate_type = "lib"]
84#![warn(missing_docs)]
85
86extern crate libc;
87extern crate time;
88
89#[macro_use]
90extern crate bitflags;
91
92#[macro_use]
93extern crate enum_primitive;
94
95use std::error::{Error};
96use std::fmt::Display;
97use std::fmt;
98
99pub use core::Access;
100pub use core::{DatabaseConnection, PreparedStatement, ResultSet, ResultRow};
101pub use core::{ColIx, ParamIx};
102pub use types::{FromSql, ToSql};
103
104use self::SqliteErrorCode::SQLITE_MISUSE;
105
106pub mod core;
107pub mod types;
108
109#[allow(non_camel_case_types, non_snake_case)]
111#[allow(dead_code)]
112#[allow(missing_docs)]
113#[allow(missing_copy_implementations)] pub mod ffi;
115
116pub mod access;
117
118pub trait StatementUpdate {
120 fn update(&mut self,
122 values: &[&ToSql]) -> SqliteResult<u64>;
123}
124
125
126impl StatementUpdate for core::PreparedStatement {
127 fn update(&mut self,
138 values: &[&ToSql]) -> SqliteResult<u64> {
139 let check = {
140 try!(bind_values(self, values));
141 let mut results = self.execute();
142 match try!(results.step()) {
143 None => Ok(()),
144 Some(_row) => Err(SqliteError {
145 kind: SQLITE_MISUSE,
146 desc: "unexpected SQLITE_ROW from update",
147 detail: None
148 })
149 }
150 };
151 check.map(|_ok| self.changes())
152 }
153}
154
155
156pub trait QueryEach<F>
158 where F: FnMut(&mut ResultRow) -> SqliteResult<()>
159{
160 fn query_each(&mut self,
162 values: &[&ToSql],
163 each_row: &mut F
164 ) -> SqliteResult<()>;
165}
166
167impl<F> QueryEach<F> for core::PreparedStatement
168 where F: FnMut(&mut ResultRow) -> SqliteResult<()>
169{
170 fn query_each(&mut self,
175 values: &[&ToSql],
176 each_row: &mut F
177 ) -> SqliteResult<()>
178 {
179 try!(bind_values(self, values));
180 let mut results = self.execute();
181 loop {
182 match try!(results.step()) {
183 None => break,
184 Some(ref mut row) => try!(each_row(row)),
185 }
186 }
187 Ok(())
188 }
189}
190
191
192pub trait QueryFold<F, A>
194 where F: Fn(&mut ResultRow, A) -> SqliteResult<A>
195{
196 fn query_fold(&mut self,
198 values: &[&ToSql],
199 init: A,
200 each_row: F
201 ) -> SqliteResult<A>;
202}
203
204
205impl<F, A> QueryFold<F, A> for core::PreparedStatement
206 where F: Fn(&mut ResultRow, A) -> SqliteResult<A>
207{
208 fn query_fold(&mut self,
210 values: &[&ToSql],
211 init: A,
212 f: F
213 ) -> SqliteResult<A>
214 {
215 try!(bind_values(self, values));
216 let mut results = self.execute();
217 let mut accum = init;
218 loop {
219 match try!(results.step()) {
220 None => break,
221 Some(ref mut row) => accum = try!(f(row, accum)),
222 }
223 }
224 Ok(accum)
225 }
226}
227
228
229pub trait Query<F, T>
231 where F: FnMut(&mut ResultRow) -> SqliteResult<T>
232{
233 fn query<'stmt>(&'stmt mut self,
241 values: &[&ToSql],
242 txform: F
243 ) -> SqliteResult<QueryResults<'stmt, T, F>>;
244}
245
246impl<F, T> Query<F, T> for core::PreparedStatement
247 where F: FnMut(&mut ResultRow) -> SqliteResult<T>
248{
249 fn query<'stmt>(&'stmt mut self,
250 values: &[&ToSql],
251 txform: F
252 ) -> SqliteResult<QueryResults<'stmt, T, F>>
253 {
254 try!(bind_values(self, values));
255 let results = self.execute();
256 Ok(QueryResults { results: results, txform: txform })
257 }
258}
259
260pub struct QueryResults<'stmt, T, F>
262 where F: FnMut(&mut ResultRow) -> SqliteResult<T>
263{
264 results: core::ResultSet<'stmt>,
265 txform: F
266}
267
268impl<'stmt, T, F> Iterator for QueryResults<'stmt, T, F>
269 where F: FnMut(&mut ResultRow) -> SqliteResult<T>
270{
271 type Item = SqliteResult<T>;
272
273 fn next(&mut self) -> Option<SqliteResult<T>> {
274 match self.results.step() {
275 Ok(None) => None,
276 Ok(Some(ref mut row)) => Some((self.txform)(row)),
277 Err(e) => Some(Err(e))
278 }
279 }
280}
281
282
283fn bind_values(s: &mut PreparedStatement, values: &[&ToSql]) -> SqliteResult<()> {
284 for (ix, v) in values.iter().enumerate() {
285 let p = ix as ParamIx + 1;
286 try!(v.to_sql(s, p));
287 }
288 Ok(())
289}
290
291
292pub trait ResultRowAccess {
294 fn get<I: RowIndex + Display + Clone, T: FromSql>(&mut self, idx: I) -> T;
300
301 fn get_opt<I: RowIndex + Display + Clone, T: FromSql>(&mut self, idx: I) -> SqliteResult<T>;
303}
304
305impl<'res, 'row> ResultRowAccess for core::ResultRow<'res, 'row> {
306 fn get<I: RowIndex + Display + Clone, T: FromSql>(&mut self, idx: I) -> T {
307 match self.get_opt(idx.clone()) {
308 Ok(ok) => ok,
309 Err(err) => panic!("retrieving column {}: {}", idx, err)
310 }
311 }
312
313 fn get_opt<I: RowIndex + Display + Clone, T: FromSql>(&mut self, idx: I) -> SqliteResult<T> {
314 match idx.idx(self) {
315 Some(idx) => FromSql::from_sql(self, idx),
316 None => Err(SqliteError {
317 kind: SQLITE_MISUSE,
318 desc: "no such row name/number",
319 detail: Some(format!("{}", idx))
320 })
321 }
322 }
323
324}
325
326pub trait RowIndex {
331 fn idx(&self, row: &mut ResultRow) -> Option<ColIx>;
333}
334
335impl RowIndex for ColIx {
336 fn idx(&self, _row: &mut ResultRow) -> Option<ColIx> { Some(*self) }
338}
339
340impl RowIndex for &'static str {
341 fn idx(&self, row: &mut ResultRow) -> Option<ColIx> {
346 let mut ixs = 0 .. row.column_count();
347 ixs.find(|ix| row.with_column_name(*ix, false, |name| name == *self))
348 }
349}
350
351
352#[must_use]
354pub type SqliteResult<T> = Result<T, SqliteError>;
355
356enum_from_primitive! {
367 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
368 #[allow(non_camel_case_types)]
369 #[allow(missing_docs)]
370 pub enum SqliteErrorCode {
371 SQLITE_ERROR = 1,
372 SQLITE_INTERNAL = 2,
373 SQLITE_PERM = 3,
374 SQLITE_ABORT = 4,
375 SQLITE_BUSY = 5,
376 SQLITE_LOCKED = 6,
377 SQLITE_NOMEM = 7,
378 SQLITE_READONLY = 8,
379 SQLITE_INTERRUPT = 9,
380 SQLITE_IOERR = 10,
381 SQLITE_CORRUPT = 11,
382 SQLITE_NOTFOUND = 12,
383 SQLITE_FULL = 13,
384 SQLITE_CANTOPEN = 14,
385 SQLITE_PROTOCOL = 15,
386 SQLITE_EMPTY = 16,
387 SQLITE_SCHEMA = 17,
388 SQLITE_TOOBIG = 18,
389 SQLITE_CONSTRAINT= 19,
390 SQLITE_MISMATCH = 20,
391 SQLITE_MISUSE = 21,
392 SQLITE_NOLFS = 22,
393 SQLITE_AUTH = 23,
394 SQLITE_FORMAT = 24,
395 SQLITE_RANGE = 25,
396 SQLITE_NOTADB = 26
397 }
398}
399
400#[derive(Debug, PartialEq, Eq)]
402pub struct SqliteError {
403 pub kind: SqliteErrorCode,
405 pub desc: &'static str,
407 pub detail: Option<String>
409}
410
411impl Display for SqliteError {
412 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
413 match self.detail {
414 Some(ref x) => f.write_fmt(format_args!("{} ({})", x, self.kind as u32)),
415 None => f.write_fmt(format_args!("{} ({})", self.desc, self.kind as u32))
416 }
417 }
418}
419
420impl SqliteError {
421 pub fn detail(&self) -> Option<String> { self.detail.clone() }
423}
424
425impl Error for SqliteError {
426 fn description(&self) -> &str { self.desc }
427 fn cause(&self) -> Option<&Error> { None }
428}
429
430
431enum_from_primitive! {
433 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
434 #[allow(non_camel_case_types)]
435 #[allow(missing_docs)]
436 pub enum ColumnType {
437 SQLITE_INTEGER = 1,
438 SQLITE_FLOAT = 2,
439 SQLITE_TEXT = 3,
440 SQLITE_BLOB = 4,
441 SQLITE_NULL = 5
442 }
443}
444
445#[cfg(test)]
446mod bind_tests {
447 use super::{DatabaseConnection, ResultSet};
448 use super::{ResultRowAccess};
449 use super::{SqliteResult};
450
451 #[test]
452 fn bind_fun() {
453 fn go() -> SqliteResult<()> {
454 let mut database = try!(DatabaseConnection::in_memory());
455
456 try!(database.exec(
457 "BEGIN;
458 CREATE TABLE test (id int, name text, address text);
459 INSERT INTO test (id, name, address) VALUES (1, 'John Doe', '123 w Pine');
460 COMMIT;"));
461
462 {
463 let mut tx = try!(database.prepare(
464 "INSERT INTO test (id, name, address) VALUES (?, ?, ?)"));
465 assert_eq!(tx.bind_parameter_count(), 3);
466 try!(tx.bind_int(1, 2));
467 try!(tx.bind_text(2, "Jane Doe"));
468 try!(tx.bind_text(3, "345 e Walnut"));
469 let mut results = tx.execute();
470 assert!(results.step().ok().unwrap().is_none());
471 }
472 assert_eq!(database.changes(), 1);
473
474 let mut q = try!(database.prepare("select * from test order by id"));
475 let mut rows = q.execute();
476 match rows.step() {
477 Ok(Some(ref mut row)) => {
478 assert_eq!(row.get::<u32, i32>(0), 1);
479 },
482 _ => panic!()
483 }
484
485 match rows.step() {
486 Ok(Some(ref mut row)) => {
487 assert_eq!(row.get::<u32, i32>(0), 2);
488 },
491 _ => panic!()
492 }
493 Ok(())
494 }
495 match go() {
496 Ok(_) => (),
497 Err(e) => panic!("oops! {:?}", e)
498 }
499 }
500
501 fn with_query<T, F>(sql: &str, mut f: F) -> SqliteResult<T>
502 where F: FnMut(&mut ResultSet) -> T
503 {
504 let db = try!(DatabaseConnection::in_memory());
505 let mut s = try!(db.prepare(sql));
506 let mut rows = s.execute();
507 let x = f(&mut rows);
508 return Ok(x);
509 }
510
511 #[test]
512 fn named_rowindex() {
513 fn go() -> SqliteResult<(u32, i32)> {
514 let mut count = 0;
515 let mut sum = 0i32;
516
517 with_query("select 1 as col1
518 union all
519 select 2", |rows| {
520 loop {
521 match rows.step() {
522 Ok(Some(ref mut row)) => {
523 count += 1;
524 sum += row.column_int(0);
525 },
526 _ => break
527 }
528 }
529 (count, sum)
530 })
531 }
532 assert_eq!(go(), Ok((2, 3)))
533 }
534
535 #[test]
536 fn err_with_detail() {
537 let io = || {
538 let mut conn = try!(DatabaseConnection::in_memory());
539 conn.exec("CREATE gobbledygook")
540 };
541
542 let go = || match io() {
543 Ok(_) => panic!(),
544 Err(oops) => {
545 format!("{:?}: {}: {}",
546 oops.kind, oops.desc,
547 oops.detail.unwrap())
548 }
549 };
550
551 let expected = "SQLITE_ERROR: sqlite3_exec: near \"gobbledygook\": syntax error";
552 assert_eq!(go(), expected.to_string())
553 }
554}