From ea9eff2fd25f41534f92613cfc91719cf012c233 Mon Sep 17 00:00:00 2001 From: Ivan Ukhov Date: Sat, 1 Aug 2015 16:23:05 -0400 Subject: [PATCH] Introduce Type and Statement::kind for reading it --- README.md | 12 ++-- src/connection.rs | 21 ------ src/lib.rs | 27 ++++++-- src/statement.rs | 19 +++++- tests/lib.rs | 160 +++++++++++++++++++++++++++++----------------- 5 files changed, 147 insertions(+), 92 deletions(-) diff --git a/README.md b/README.md index 012d7e4..acde497 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,11 @@ The package provides an interface to [SQLite][1]. let connection = sqlite::open(":memory:").unwrap(); connection.execute(" - CREATE TABLE `users` (id INTEGER, name VARCHAR(255)); - INSERT INTO `users` (id, name) VALUES (42, 'Alice'); + CREATE TABLE users (id INTEGER, name VARCHAR(255)); + INSERT INTO users (id, name) VALUES (42, 'Alice'); ").unwrap(); -connection.process("SELECT * FROM `users`", |pairs| { +connection.process("SELECT * FROM users", |pairs| { for &(column, value) in pairs.iter() { println!("{} = {}", column, value.unwrap()); } @@ -30,17 +30,17 @@ use sqlite::State; let connection = sqlite::open(":memory:").unwrap(); connection.execute(" - CREATE TABLE `users` (id INTEGER, name VARCHAR(255)) + CREATE TABLE users (id INTEGER, name VARCHAR(255)) "); let mut statement = connection.prepare(" - INSERT INTO `users` (id, name) VALUES (?, ?) + INSERT INTO users (id, name) VALUES (?, ?) ").unwrap(); statement.bind(1, 42).unwrap(); statement.bind(2, "Alice").unwrap(); assert_eq!(statement.step().unwrap(), State::Done); -let mut statement = connection.prepare("SELECT * FROM `users`").unwrap(); +let mut statement = connection.prepare("SELECT * FROM users").unwrap(); while let State::Row = statement.step().unwrap() { println!("id = {}", statement.read::(0).unwrap()); println!("name = {}", statement.read::(1).unwrap()); diff --git a/src/connection.rs b/src/connection.rs index c5b4a16..31d1837 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -146,24 +146,3 @@ extern fn process_callback(callback: *mut c_void, count: c_int, values: *mut if (*(callback as *mut F))(&pairs) { 0 } else { 1 } } } - -#[cfg(test)] -mod tests { - use super::Connection; - - #[test] - fn execute() { - let connection = Connection::open(":memory:").unwrap(); - match connection.execute(":)") { - Err(error) => assert_eq!(error.message, - Some(String::from(r#"unrecognized token: ":""#))), - _ => unreachable!(), - } - } - - #[test] - fn set_busy_handler() { - let mut connection = Connection::open(":memory:").unwrap(); - connection.set_busy_handler(|_| true).unwrap(); - } -} diff --git a/src/lib.rs b/src/lib.rs index 1690d8e..7da98df 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,11 +6,11 @@ //! let connection = sqlite::open(":memory:").unwrap(); //! //! connection.execute(" -//! CREATE TABLE `users` (id INTEGER, name VARCHAR(255)); -//! INSERT INTO `users` (id, name) VALUES (42, 'Alice'); +//! CREATE TABLE users (id INTEGER, name VARCHAR(255)); +//! INSERT INTO users (id, name) VALUES (42, 'Alice'); //! ").unwrap(); //! -//! connection.process("SELECT * FROM `users`", |pairs| { +//! connection.process("SELECT * FROM users", |pairs| { //! for &(column, value) in pairs.iter() { //! println!("{} = {}", column, value.unwrap()); //! } @@ -26,17 +26,17 @@ //! let connection = sqlite::open(":memory:").unwrap(); //! //! connection.execute(" -//! CREATE TABLE `users` (id INTEGER, name VARCHAR(255)) +//! CREATE TABLE users (id INTEGER, name VARCHAR(255)) //! "); //! //! let mut statement = connection.prepare(" -//! INSERT INTO `users` (id, name) VALUES (?, ?) +//! INSERT INTO users (id, name) VALUES (?, ?) //! ").unwrap(); //! statement.bind(1, 42).unwrap(); //! statement.bind(2, "Alice").unwrap(); //! assert_eq!(statement.step().unwrap(), State::Done); //! -//! let mut statement = connection.prepare("SELECT * FROM `users`").unwrap(); +//! let mut statement = connection.prepare("SELECT * FROM users").unwrap(); //! while let State::Row = statement.step().unwrap() { //! println!("id = {}", statement.read::(0).unwrap()); //! println!("name = {}", statement.read::(1).unwrap()); @@ -98,6 +98,21 @@ macro_rules! str_to_cstr( }); ); +/// A data type. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Type { + /// Binary data. + Blob, + /// A 64-bit IEEE floating-point number. + Float, + /// A 64-bit signed integer. + Integer, + /// An absence of a value. + Null, + /// A string. + String, +} + mod connection; mod error; mod statement; diff --git a/src/statement.rs b/src/statement.rs index a2e4799..dca27b1 100644 --- a/src/statement.rs +++ b/src/statement.rs @@ -2,7 +2,7 @@ use ffi; use libc::{c_double, c_int}; use std::marker::PhantomData; -use Result; +use {Result, Type}; /// A prepared statement. pub struct Statement<'l> { @@ -38,10 +38,25 @@ pub trait Value { impl<'l> Statement<'l> { /// Return the number of columns. #[inline] - pub fn columns(&mut self) -> usize { + pub fn columns(&self) -> usize { unsafe { ffi::sqlite3_column_count(self.raw.0) as usize } } + /// Return the type of a column. + /// + /// The type is revealed after the first step has been taken. + #[inline] + pub fn kind(&self, i: usize) -> Type { + match unsafe { ffi::sqlite3_column_type(self.raw.0, i as c_int) } { + ffi::SQLITE_BLOB => Type::Blob, + ffi::SQLITE_FLOAT => Type::Float, + ffi::SQLITE_INTEGER => Type::Integer, + ffi::SQLITE_NULL => Type::Null, + ffi::SQLITE_TEXT => Type::String, + _ => unreachable!(), + } + } + /// Bind the parameter at a specific location. /// /// The leftmost location has the index 1. diff --git a/tests/lib.rs b/tests/lib.rs index 97628ba..df5e4f1 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,82 +1,61 @@ extern crate sqlite; extern crate temporary; +use sqlite::{Connection, State, Type}; +use std::path::Path; + macro_rules! ok( ($result:expr) => ($result.unwrap()); ); #[test] -fn workflow() { - use sqlite::State; - - macro_rules! pair( - ($one:expr, $two:expr) => (($one, Some($two))); - ); - - let connection = ok!(sqlite::open(":memory:")); - - let sql = "CREATE TABLE `users` (id INTEGER, name VARCHAR(255), age REAL)"; - ok!(connection.execute(sql)); - - { - let sql = "INSERT INTO `users` (id, name, age) VALUES (?, ?, ?)"; - let mut statement = ok!(connection.prepare(sql)); - assert_eq!(statement.columns(), 0); - ok!(statement.bind(1, 1i64)); - ok!(statement.bind(2, "Alice")); - ok!(statement.bind(3, 20.99)); - assert_eq!(ok!(statement.step()), State::Done); - } - - { - let mut done = false; - let sql = "SELECT * FROM `users`"; - ok!(connection.process(sql, |pairs| { - assert_eq!(pairs.len(), 3); - assert_eq!(pairs[0], pair!("id", "1")); - assert_eq!(pairs[1], pair!("name", "Alice")); - assert_eq!(pairs[2], pair!("age", "20.99")); - done = true; - true - })); - assert!(done); - } - - { - let sql = "SELECT * FROM `users`"; - let mut statement = ok!(connection.prepare(sql)); - assert_eq!(statement.columns(), 3); - assert_eq!(ok!(statement.step()), State::Row); - assert_eq!(ok!(statement.read::(0)), 1); - assert_eq!(ok!(statement.read::(1)), String::from("Alice")); - assert_eq!(ok!(statement.read::(2)), 20.99); - assert_eq!(ok!(statement.step()), State::Done); +fn connection_execute() { + let connection = setup(":memory:"); + match connection.execute(":)") { + Err(error) => assert_eq!(error.message, Some(String::from(r#"unrecognized token: ":""#))), + _ => unreachable!(), } } #[test] -fn stress() { - use sqlite::State; - use std::path::PathBuf; +fn connection_process() { + macro_rules! pair( + ($one:expr, $two:expr) => (($one, Some($two))); + ); + + let connection = setup(":memory:"); + + let mut done = false; + let query = "SELECT * FROM users"; + ok!(connection.process(query, |pairs| { + assert_eq!(pairs.len(), 3); + assert_eq!(pairs[0], pair!("id", "1")); + assert_eq!(pairs[1], pair!("name", "Alice")); + assert_eq!(pairs[2], pair!("age", "42.69")); + done = true; + true + })); + assert!(done); +} + +#[test] +fn connection_set_busy_handler() { use std::thread; use temporary::Directory; let directory = ok!(Directory::new("sqlite")); let path = directory.path().join("database.sqlite3"); - - let connection = ok!(sqlite::open(&path)); - let sql = "CREATE TABLE `users` (id INTEGER, name VARCHAR(255), age REAL)"; - ok!(connection.execute(sql)); + setup(&path); let guards = (0..100).map(|_| { - let path = PathBuf::from(&path); + let path = path.to_path_buf(); thread::spawn(move || { let mut connection = ok!(sqlite::open(&path)); ok!(connection.set_busy_handler(|_| true)); - let sql = "INSERT INTO `users` (id, name, age) VALUES (?, ?, ?)"; - let mut statement = ok!(connection.prepare(sql)); - ok!(statement.bind(1, 1i64)); - ok!(statement.bind(2, "Alice")); + let query = "INSERT INTO `users` (id, name, age) VALUES (?, ?, ?)"; + let mut statement = ok!(connection.prepare(query)); + ok!(statement.bind(1, 2i64)); + ok!(statement.bind(2, "Bob")); ok!(statement.bind(3, 20.99)); assert_eq!(ok!(statement.step()), State::Done); true @@ -87,3 +66,70 @@ fn stress() { assert!(guard.join().unwrap()); } } + +#[test] +fn statement_columns() { + let connection = setup(":memory:"); + let query = "SELECT * FROM users"; + let mut statement = ok!(connection.prepare(query)); + + assert_eq!(statement.columns(), 3); + + assert_eq!(ok!(statement.step()), State::Row); + + assert_eq!(statement.columns(), 3); +} + +#[test] +fn statement_kind() { + let connection = setup(":memory:"); + let query = "SELECT * FROM users"; + let mut statement = ok!(connection.prepare(query)); + + assert_eq!(statement.kind(0), Type::Null); + assert_eq!(statement.kind(1), Type::Null); + assert_eq!(statement.kind(2), Type::Null); + + assert_eq!(ok!(statement.step()), State::Row); + + assert_eq!(statement.kind(0), Type::Integer); + assert_eq!(statement.kind(1), Type::String); + assert_eq!(statement.kind(2), Type::Float); +} + +#[test] +fn statement_insert() { + let connection = setup(":memory:"); + let query = "INSERT INTO users (id, name, age) VALUES (?, ?, ?)"; + let mut statement = ok!(connection.prepare(query)); + + ok!(statement.bind(1, 2i64)); + ok!(statement.bind(2, "Bob")); + ok!(statement.bind(3, 20.99)); + assert_eq!(ok!(statement.step()), State::Done); +} + +#[test] +fn statement_select() { + let connection = setup(":memory:"); + let query = "SELECT * FROM users"; + let mut statement = ok!(connection.prepare(query)); + + assert_eq!(ok!(statement.step()), State::Row); + assert_eq!(ok!(statement.read::(0)), 1); + assert_eq!(ok!(statement.read::(1)), String::from("Alice")); + assert_eq!(ok!(statement.read::(2)), 42.69); + assert_eq!(ok!(statement.step()), State::Done); +} + +fn setup>(path: T) -> Connection { + let connection = ok!(sqlite::open(path)); + + let query = "CREATE TABLE users (id INTEGER, name VARCHAR(255), age REAL)"; + ok!(connection.execute(query)); + + let query = "INSERT INTO users (id, name, age) VALUES (1, 'Alice', 42.69)"; + ok!(connection.execute(query)); + + connection +}