diff --git a/src/cache.rs b/src/cache.rs index 203fbce..37105dc 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -5,6 +5,7 @@ use std::ops::{Deref, DerefMut}; use lru_cache::LruCache; use {Result, Connection, Statement}; use raw_statement::RawStatement; +use statement::StatementCrateImpl; impl Connection { /// Prepare a SQL statement for execution, returning a previously prepared (but diff --git a/src/convenient.rs b/src/convenient.rs deleted file mode 100644 index 4740aa2..0000000 --- a/src/convenient.rs +++ /dev/null @@ -1,120 +0,0 @@ -use {Error, Result, Row, Statement}; -use types::ToSql; - -impl<'conn> Statement<'conn> { - /// Execute an INSERT and return the ROWID. - /// - /// # Note - /// - /// This function is a convenience wrapper around `execute()` intended for queries that - /// insert a single item. It is possible to misuse this function in a way that it cannot - /// detect, such as by calling it on a statement which _updates_ a single item rather than - /// inserting one. Please don't do that. - /// - /// # Failure - /// - /// Will return `Err` if no row is inserted or many rows are inserted. - pub fn insert(&mut self, params: &[&ToSql]) -> Result { - let changes = try!(self.execute(params)); - match changes { - 1 => Ok(self.conn.last_insert_rowid()), - _ => Err(Error::StatementChangedRows(changes)), - } - } - - /// Return `true` if a query in the SQL statement it executes returns one or more rows - /// and `false` if the SQL returns an empty set. - pub fn exists(&mut self, params: &[&ToSql]) -> Result { - let mut rows = try!(self.query(params)); - let exists = { - match rows.next() { - Some(_) => true, - None => false, - } - }; - Ok(exists) - } - - /// Convenience method to execute a query that is expected to return a single row. - /// - /// If the query returns more than one row, all rows except the first are ignored. - /// - /// # Failure - /// - /// Will return `Err` if the underlying SQLite call fails. - pub fn query_row(&mut self, params: &[&ToSql], f: F) -> Result - where F: FnOnce(&Row) -> T - { - let mut rows = try!(self.query(params)); - - rows.get_expected_row().map(|r| f(&r)) - } -} - -#[cfg(test)] -mod test { - use {Connection, Error, Result}; - - #[test] - fn test_insert() { - let db = Connection::open_in_memory().unwrap(); - db.execute_batch("CREATE TABLE foo(x INTEGER UNIQUE)").unwrap(); - let mut stmt = db.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?)").unwrap(); - assert_eq!(stmt.insert(&[&1i32]).unwrap(), 1); - assert_eq!(stmt.insert(&[&2i32]).unwrap(), 2); - match stmt.insert(&[&1i32]).unwrap_err() { - Error::StatementChangedRows(0) => (), - err => panic!("Unexpected error {}", err), - } - let mut multi = db.prepare("INSERT INTO foo (x) SELECT 3 UNION ALL SELECT 4").unwrap(); - match multi.insert(&[]).unwrap_err() { - Error::StatementChangedRows(2) => (), - err => panic!("Unexpected error {}", err), - } - } - - #[test] - fn test_insert_different_tables() { - // Test for https://github.com/jgallagher/rusqlite/issues/171 - let db = Connection::open_in_memory().unwrap(); - db.execute_batch(r" - CREATE TABLE foo(x INTEGER); - CREATE TABLE bar(x INTEGER); - ") - .unwrap(); - - assert_eq!(db.prepare("INSERT INTO foo VALUES (10)").unwrap().insert(&[]).unwrap(), - 1); - assert_eq!(db.prepare("INSERT INTO bar VALUES (10)").unwrap().insert(&[]).unwrap(), - 1); - } - - #[test] - fn test_exists() { - let db = Connection::open_in_memory().unwrap(); - let sql = "BEGIN; - CREATE TABLE foo(x INTEGER); - INSERT INTO foo VALUES(1); - INSERT INTO foo VALUES(2); - END;"; - db.execute_batch(sql).unwrap(); - let mut stmt = db.prepare("SELECT 1 FROM foo WHERE x = ?").unwrap(); - assert!(stmt.exists(&[&1i32]).unwrap()); - assert!(stmt.exists(&[&2i32]).unwrap()); - assert!(!stmt.exists(&[&0i32]).unwrap()); - } - - #[test] - fn test_query_row() { - let db = Connection::open_in_memory().unwrap(); - let sql = "BEGIN; - CREATE TABLE foo(x INTEGER, y INTEGER); - INSERT INTO foo VALUES(1, 3); - INSERT INTO foo VALUES(2, 4); - END;"; - db.execute_batch(sql).unwrap(); - let mut stmt = db.prepare("SELECT y FROM foo WHERE x = ?").unwrap(); - let y: Result = stmt.query_row(&[&1i32], |r| r.get(0)); - assert_eq!(3i64, y.unwrap()); - } -} diff --git a/src/lib.rs b/src/lib.rs index 87c752f..ad919e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -73,13 +73,16 @@ use std::result; use std::str; use std::sync::{Once, ONCE_INIT}; use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering}; -use std::os::raw::{c_int, c_char, c_void}; +use std::os::raw::{c_int, c_char}; -use types::{ToSql, ToSqlOutput, FromSql, FromSqlError, ValueRef}; +use types::{ToSql, FromSql, FromSqlError, ValueRef}; use error::{error_from_sqlite_code, error_from_handle}; use raw_statement::RawStatement; use cache::StatementCache; +pub use statement::Statement; +use statement::StatementCrateImpl; + #[allow(deprecated)] pub use transaction::{SqliteTransaction, SqliteTransactionBehavior}; pub use transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior}; @@ -100,10 +103,9 @@ pub mod types; mod version; mod transaction; mod cache; -mod named_params; mod error; -mod convenient; mod raw_statement; +mod statement; #[cfg(feature = "load_extension")] mod load_extension_guard; #[cfg(feature = "trace")] @@ -293,6 +295,28 @@ impl Connection { self.prepare(sql).and_then(|mut stmt| stmt.execute(params)) } + /// Convenience method to prepare and execute a single SQL statement with named parameter(s). + /// + /// On success, returns the number of rows that were changed or inserted or deleted (via + /// `sqlite3_changes`). + /// + /// ## Example + /// + /// ```rust,no_run + /// # use rusqlite::{Connection, Result}; + /// fn insert(conn: &Connection) -> Result { + /// conn.execute_named("INSERT INTO test (name) VALUES (:name)", &[(":name", &"one")]) + /// } + /// ``` + /// + /// # Failure + /// + /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the + /// underlying SQLite call fails. + pub fn execute_named(&self, sql: &str, params: &[(&str, &ToSql)]) -> Result { + self.prepare(sql).and_then(|mut stmt| stmt.execute_named(params)) + } + /// Get the SQLite rowid of the most recent successful INSERT. /// /// Uses [sqlite3_last_insert_rowid](https://www.sqlite.org/c3ref/last_insert_rowid.html) under @@ -327,6 +351,24 @@ impl Connection { stmt.query_row(params, f) } + /// Convenience method to execute a query with named parameter(s) that is expected to return + /// a single row. + /// + /// If the query returns more than one row, all rows except the first are ignored. + /// + /// # Failure + /// + /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the + /// underlying SQLite call fails. + pub fn query_row_named(&self, sql: &str, params: &[(&str, &ToSql)], f: F) -> Result + where F: FnOnce(&Row) -> T + { + let mut stmt = try!(self.prepare(sql)); + let mut rows = try!(stmt.query_named(params)); + + rows.get_expected_row().map(|r| f(&r)) + } + /// Convenience method to execute a query that is expected to return a single row, /// and execute a mapping via `f` on that returned row with the possibility of failure. /// The `Result` type of `f` must implement `std::convert::From`. @@ -823,295 +865,6 @@ impl Drop for InnerConnection { #[deprecated(since = "0.6.0", note = "Use Statement instead")] pub type SqliteStatement<'conn> = Statement<'conn>; -/// A prepared statement. -pub struct Statement<'conn> { - conn: &'conn Connection, - stmt: RawStatement, -} - -impl<'conn> Statement<'conn> { - fn new(conn: &Connection, stmt: RawStatement) -> Statement { - Statement { - conn: conn, - stmt: stmt, - } - } - - /// Get all the column names in the result set of the prepared statement. - pub fn column_names(&self) -> Vec<&str> { - let n = self.column_count(); - let mut cols = Vec::with_capacity(n as usize); - for i in 0..n { - let slice = self.stmt.column_name(i); - let s = str::from_utf8(slice.to_bytes()).unwrap(); - cols.push(s); - } - cols - } - - /// Return the number of columns in the result set returned by the prepared statement. - pub fn column_count(&self) -> i32 { - self.stmt.column_count() - } - - /// Returns the column index in the result set for a given column name. - /// - /// If there is no AS clause then the name of the column is unspecified and may change from one - /// release of SQLite to the next. - /// - /// # Failure - /// - /// Will return an `Error::InvalidColumnName` when there is no column with the specified `name`. - pub fn column_index(&self, name: &str) -> Result { - let bytes = name.as_bytes(); - let n = self.column_count(); - for i in 0..n { - if bytes == self.stmt.column_name(i).to_bytes() { - return Ok(i); - } - } - Err(Error::InvalidColumnName(String::from(name))) - } - - /// Execute the prepared statement. - /// - /// On success, returns the number of rows that were changed or inserted or deleted (via - /// `sqlite3_changes`). - /// - /// ## Example - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// fn update_rows(conn: &Connection) -> Result<()> { - /// let mut stmt = try!(conn.prepare("UPDATE foo SET bar = 'baz' WHERE qux = ?")); - /// - /// try!(stmt.execute(&[&1i32])); - /// try!(stmt.execute(&[&2i32])); - /// - /// Ok(()) - /// } - /// ``` - /// - /// # Failure - /// - /// Will return `Err` if binding parameters fails, the executed statement returns rows (in - /// which case `query` should be used instead), or the underling SQLite call fails. - pub fn execute(&mut self, params: &[&ToSql]) -> Result { - try!(self.bind_parameters(params)); - self.execute_() - } - - fn execute_(&mut self) -> Result { - let r = self.stmt.step(); - self.stmt.reset(); - match r { - ffi::SQLITE_DONE => { - if self.column_count() == 0 { - Ok(self.conn.changes()) - } else { - Err(Error::ExecuteReturnedResults) - } - } - ffi::SQLITE_ROW => Err(Error::ExecuteReturnedResults), - _ => Err(self.conn.decode_result(r).unwrap_err()), - } - } - - /// Execute the prepared statement, returning a handle to the resulting rows. - /// - /// Due to lifetime restricts, the rows handle returned by `query` does not - /// implement the `Iterator` trait. Consider using `query_map` or `query_and_then` - /// instead, which do. - /// - /// ## Example - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// fn get_names(conn: &Connection) -> Result> { - /// let mut stmt = try!(conn.prepare("SELECT name FROM people")); - /// let mut rows = try!(stmt.query(&[])); - /// - /// let mut names = Vec::new(); - /// while let Some(result_row) = rows.next() { - /// let row = try!(result_row); - /// names.push(row.get(0)); - /// } - /// - /// Ok(names) - /// } - /// ``` - /// - /// ## Failure - /// - /// Will return `Err` if binding parameters fails. - pub fn query<'a>(&'a mut self, params: &[&ToSql]) -> Result> { - try!(self.bind_parameters(params)); - Ok(Rows::new(self)) - } - - /// Executes the prepared statement and maps a function over the resulting rows, returning - /// an iterator over the mapped function results. - /// - /// ## Example - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// fn get_names(conn: &Connection) -> Result> { - /// let mut stmt = try!(conn.prepare("SELECT name FROM people")); - /// let rows = try!(stmt.query_map(&[], |row| row.get(0))); - /// - /// let mut names = Vec::new(); - /// for name_result in rows { - /// names.push(try!(name_result)); - /// } - /// - /// Ok(names) - /// } - /// ``` - /// - /// ## Failure - /// - /// Will return `Err` if binding parameters fails. - pub fn query_map<'a, T, F>(&'a mut self, params: &[&ToSql], f: F) -> Result> - where F: FnMut(&Row) -> T - { - let row_iter = try!(self.query(params)); - - Ok(MappedRows { - rows: row_iter, - map: f, - }) - } - - /// Executes the prepared statement and maps a function over the resulting - /// rows, where the function returns a `Result` with `Error` type implementing - /// `std::convert::From` (so errors can be unified). - /// - /// # Failure - /// - /// Will return `Err` if binding parameters fails. - pub fn query_and_then<'a, T, E, F>(&'a mut self, - params: &[&ToSql], - f: F) - -> Result> - where E: convert::From, - F: FnMut(&Row) -> result::Result - { - let row_iter = try!(self.query(params)); - - Ok(AndThenRows { - rows: row_iter, - map: f, - }) - } - - /// Consumes the statement. - /// - /// Functionally equivalent to the `Drop` implementation, but allows callers to see any errors - /// that occur. - /// - /// # Failure - /// - /// Will return `Err` if the underlying SQLite call fails. - pub fn finalize(mut self) -> Result<()> { - self.finalize_() - } - - fn bind_parameter(&self, param: &ToSql, col: c_int) -> Result<()> { - let value = try!(param.to_sql()); - - let ptr = unsafe { self.stmt.ptr() }; - let value = match value { - ToSqlOutput::Borrowed(v) => v, - ToSqlOutput::Owned(ref v) => ValueRef::from(v), - - #[cfg(feature = "blob")] - ToSqlOutput::ZeroBlob(len) => { - return self.conn - .decode_result(unsafe { ffi::sqlite3_bind_zeroblob(ptr, col, len) }); - } - }; - self.conn.decode_result(match value { - ValueRef::Null => unsafe { ffi::sqlite3_bind_null(ptr, col) }, - ValueRef::Integer(i) => unsafe { ffi::sqlite3_bind_int64(ptr, col, i) }, - ValueRef::Real(r) => unsafe { ffi::sqlite3_bind_double(ptr, col, r) }, - ValueRef::Text(s) => unsafe { - let length = s.len(); - if length > ::std::i32::MAX as usize { - ffi::SQLITE_TOOBIG - } else { - let c_str = try!(str_to_cstring(s)); - let destructor = if length > 0 { - ffi::SQLITE_TRANSIENT() - } else { - ffi::SQLITE_STATIC() - }; - ffi::sqlite3_bind_text(ptr, col, c_str.as_ptr(), length as c_int, destructor) - } - }, - ValueRef::Blob(b) => unsafe { - let length = b.len(); - if length > ::std::i32::MAX as usize { - ffi::SQLITE_TOOBIG - } else if length == 0 { - ffi::sqlite3_bind_zeroblob(ptr, col, 0) - } else { - ffi::sqlite3_bind_blob(ptr, - col, - b.as_ptr() as *const c_void, - length as c_int, - ffi::SQLITE_TRANSIENT()) - } - }, - }) - } - - fn bind_parameters(&mut self, params: &[&ToSql]) -> Result<()> { - assert!(params.len() as c_int == self.stmt.bind_parameter_count(), - "incorrect number of parameters to query(): expected {}, got {}", - self.stmt.bind_parameter_count(), - params.len()); - - for (i, p) in params.iter().enumerate() { - try!(self.bind_parameter(*p, (i + 1) as c_int)); - } - - Ok(()) - } - - fn finalize_(&mut self) -> Result<()> { - let mut stmt = RawStatement::new(ptr::null_mut()); - mem::swap(&mut stmt, &mut self.stmt); - self.conn.decode_result(stmt.finalize()) - } -} - -impl<'conn> Into for Statement<'conn> { - fn into(mut self) -> RawStatement { - let mut stmt = RawStatement::new(ptr::null_mut()); - mem::swap(&mut stmt, &mut self.stmt); - stmt - } -} - -impl<'conn> fmt::Debug for Statement<'conn> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let sql = str::from_utf8(self.stmt.sql().to_bytes()); - f.debug_struct("Statement") - .field("conn", self.conn) - .field("stmt", &self.stmt) - .field("sql", &sql) - .finish() - } -} - -impl<'conn> Drop for Statement<'conn> { - #[allow(unused_must_use)] - fn drop(&mut self) { - self.finalize_(); - } -} - /// An iterator over the mapped resulting rows of a query. pub struct MappedRows<'stmt, F> { rows: Rows<'stmt>, @@ -1175,7 +928,7 @@ impl<'stmt> Rows<'stmt> { fn reset(&mut self) { if let Some(stmt) = self.stmt.take() { - stmt.stmt.reset(); + stmt.reset(); } } @@ -1190,20 +943,18 @@ impl<'stmt> Rows<'stmt> { /// "streaming iterator". For a more natural interface, consider using `query_map` /// or `query_and_then` instead, which return types that implement `Iterator`. pub fn next<'a>(&'a mut self) -> Option>> { - self.stmt.and_then(|stmt| match stmt.stmt.step() { - ffi::SQLITE_ROW => { - Some(Ok(Row { - stmt: stmt, - phantom: PhantomData, - })) - } - ffi::SQLITE_DONE => { + self.stmt.and_then(|stmt| match stmt.step() { + Ok(true) => Some(Ok(Row { + stmt: stmt, + phantom: PhantomData, + })), + Ok(false) => { self.reset(); None } - code => { + Err(err) => { self.reset(); - Some(Err(stmt.conn.decode_result(code).unwrap_err())) + Some(Err(err)) } }) } @@ -1253,7 +1004,7 @@ impl<'a, 'stmt> Row<'a, 'stmt> { /// for this row. pub fn get_checked(&self, idx: I) -> Result { let idx = try!(idx.idx(self.stmt)); - let value = unsafe { ValueRef::new(&self.stmt.stmt, idx) }; + let value = self.stmt.value_ref(idx); FromSql::column_result(value).map_err(|err| match err { FromSqlError::InvalidType => Error::InvalidColumnType(idx, value.data_type()), FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i), @@ -1294,47 +1045,6 @@ impl<'a> RowIndex for &'a str { } } -impl<'a> ValueRef<'a> { - unsafe fn new(stmt: &RawStatement, col: c_int) -> ValueRef { - use std::slice::from_raw_parts; - - let raw = stmt.ptr(); - - match stmt.column_type(col) { - ffi::SQLITE_NULL => ValueRef::Null, - ffi::SQLITE_INTEGER => ValueRef::Integer(ffi::sqlite3_column_int64(raw, col)), - ffi::SQLITE_FLOAT => ValueRef::Real(ffi::sqlite3_column_double(raw, col)), - ffi::SQLITE_TEXT => { - let text = ffi::sqlite3_column_text(raw, col); - assert!(!text.is_null(), - "unexpected SQLITE_TEXT column type with NULL data"); - let s = CStr::from_ptr(text as *const c_char); - - // sqlite3_column_text returns UTF8 data, so our unwrap here should be fine. - let s = s.to_str().expect("sqlite3_column_text returned invalid UTF-8"); - ValueRef::Text(s) - } - ffi::SQLITE_BLOB => { - let blob = ffi::sqlite3_column_blob(raw, col); - - let len = ffi::sqlite3_column_bytes(raw, col); - assert!(len >= 0, - "unexpected negative return from sqlite3_column_bytes"); - if len > 0 { - assert!(!blob.is_null(), - "unexpected SQLITE_BLOB column type with NULL data"); - ValueRef::Blob(from_raw_parts(blob as *const u8, len as usize)) - } else { - // The return value from sqlite3_column_blob() for a zero-length BLOB - // is a NULL pointer. - ValueRef::Blob(&[]) - } - } - _ => unreachable!("sqlite3_column_type returned invalid value"), - } - } -} - #[cfg(test)] mod test { extern crate tempdir; diff --git a/src/named_params.rs b/src/named_params.rs deleted file mode 100644 index d6f8382..0000000 --- a/src/named_params.rs +++ /dev/null @@ -1,356 +0,0 @@ -use std::convert; -use std::result; -use std::os::raw::c_int; - -use {Result, Error, Connection, Statement, MappedRows, AndThenRows, Rows, Row, str_to_cstring}; -use types::ToSql; - -impl Connection { - /// Convenience method to prepare and execute a single SQL statement with named parameter(s). - /// - /// On success, returns the number of rows that were changed or inserted or deleted (via - /// `sqlite3_changes`). - /// - /// ## Example - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// fn insert(conn: &Connection) -> Result { - /// conn.execute_named("INSERT INTO test (name) VALUES (:name)", &[(":name", &"one")]) - /// } - /// ``` - /// - /// # Failure - /// - /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the - /// underlying SQLite call fails. - pub fn execute_named(&self, sql: &str, params: &[(&str, &ToSql)]) -> Result { - self.prepare(sql).and_then(|mut stmt| stmt.execute_named(params)) - } - - /// Convenience method to execute a query with named parameter(s) that is expected to return - /// a single row. - /// - /// If the query returns more than one row, all rows except the first are ignored. - /// - /// # Failure - /// - /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the - /// underlying SQLite call fails. - pub fn query_row_named(&self, sql: &str, params: &[(&str, &ToSql)], f: F) -> Result - where F: FnOnce(&Row) -> T - { - let mut stmt = try!(self.prepare(sql)); - let mut rows = try!(stmt.query_named(params)); - - rows.get_expected_row().map(|r| f(&r)) - } -} - -impl<'conn> Statement<'conn> { - /// Return the index of an SQL parameter given its name. - /// - /// # Failure - /// - /// Will return Err if `name` is invalid. Will return Ok(None) if the name - /// is valid but not a bound parameter of this statement. - pub fn parameter_index(&self, name: &str) -> Result> { - let c_name = try!(str_to_cstring(name)); - Ok(self.stmt.bind_parameter_index(&c_name)) - } - - /// Execute the prepared statement with named parameter(s). If any parameters - /// that were in the prepared statement are not included in `params`, they - /// will continue to use the most-recently bound value from a previous call - /// to `execute_named`, or `NULL` if they have never been bound. - /// - /// On success, returns the number of rows that were changed or inserted or deleted (via - /// `sqlite3_changes`). - /// - /// ## Example - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// fn insert(conn: &Connection) -> Result { - /// let mut stmt = try!(conn.prepare("INSERT INTO test (name) VALUES (:name)")); - /// stmt.execute_named(&[(":name", &"one")]) - /// } - /// ``` - /// - /// # Failure - /// - /// Will return `Err` if binding parameters fails, the executed statement returns rows (in - /// which case `query` should be used instead), or the underling SQLite call fails. - pub fn execute_named(&mut self, params: &[(&str, &ToSql)]) -> Result { - try!(self.bind_parameters_named(params)); - self.execute_() - } - - /// Execute the prepared statement with named parameter(s), returning a handle for the - /// resulting rows. If any parameters that were in the prepared statement are not included in - /// `params`, they will continue to use the most-recently bound value from a previous call to - /// `query_named`, or `NULL` if they have never been bound. - /// - /// ## Example - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// fn query(conn: &Connection) -> Result<()> { - /// let mut stmt = try!(conn.prepare("SELECT * FROM test where name = :name")); - /// let mut rows = try!(stmt.query_named(&[(":name", &"one")])); - /// while let Some(row) = rows.next() { - /// // ... - /// } - /// Ok(()) - /// } - /// ``` - /// - /// # Failure - /// - /// Will return `Err` if binding parameters fails. - pub fn query_named<'a>(&'a mut self, params: &[(&str, &ToSql)]) -> Result> { - try!(self.bind_parameters_named(params)); - Ok(Rows::new(self)) - } - - /// Execute the prepared statement with named parameter(s), returning an iterator over the - /// result of calling the mapping function over the query's rows. If any parameters that were - /// in the prepared statement are not included in `params`, they will continue to use the - /// most-recently bound value from a previous call to `query_named`, or `NULL` if they have - /// never been bound. - /// - /// ## Example - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// fn get_names(conn: &Connection) -> Result> { - /// let mut stmt = try!(conn.prepare("SELECT name FROM people WHERE id = :id")); - /// let rows = try!(stmt.query_map_named(&[(":id", &"one")], |row| row.get(0))); - /// - /// let mut names = Vec::new(); - /// for name_result in rows { - /// names.push(try!(name_result)); - /// } - /// - /// Ok(names) - /// } - /// ``` - /// - /// ## Failure - /// - /// Will return `Err` if binding parameters fails. - pub fn query_map_named<'a, T, F>(&'a mut self, - params: &[(&str, &ToSql)], - f: F) - -> Result> - where F: FnMut(&Row) -> T - { - let rows = try!(self.query_named(params)); - Ok(MappedRows { - rows: rows, - map: f, - }) - } - - /// Execute the prepared statement with named parameter(s), returning an iterator over the - /// result of calling the mapping function over the query's rows. If any parameters that were - /// in the prepared statement are not included in `params`, they will continue to use the - /// most-recently bound value from a previous call to `query_named`, or `NULL` if they have - /// never been bound. - /// - /// ## Example - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// struct Person { name: String }; - /// - /// fn name_to_person(name: String) -> Result { - /// // ... check for valid name - /// Ok(Person{ name: name }) - /// } - /// - /// fn get_names(conn: &Connection) -> Result> { - /// let mut stmt = try!(conn.prepare("SELECT name FROM people WHERE id = :id")); - /// let rows = try!(stmt.query_and_then_named(&[(":id", &"one")], |row| { - /// name_to_person(row.get(0)) - /// })); - /// - /// let mut persons = Vec::new(); - /// for person_result in rows { - /// persons.push(try!(person_result)); - /// } - /// - /// Ok(persons) - /// } - /// ``` - /// - /// ## Failure - /// - /// Will return `Err` if binding parameters fails. - pub fn query_and_then_named<'a, T, E, F>(&'a mut self, - params: &[(&str, &ToSql)], - f: F) - -> Result> - where E: convert::From, - F: FnMut(&Row) -> result::Result - { - let rows = try!(self.query_named(params)); - Ok(AndThenRows { - rows: rows, - map: f, - }) - } - - fn bind_parameters_named(&mut self, params: &[(&str, &ToSql)]) -> Result<()> { - for &(name, value) in params { - if let Some(i) = try!(self.parameter_index(name)) { - try!(self.bind_parameter(value, i)); - } else { - return Err(Error::InvalidParameterName(name.into())); - } - } - Ok(()) - } -} - -#[cfg(test)] -mod test { - use Connection; - use error::Error; - - #[test] - fn test_execute_named() { - let db = Connection::open_in_memory().unwrap(); - db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap(); - - assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)]).unwrap(), - 1); - assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)]).unwrap(), - 1); - - assert_eq!(3i32, - db.query_row_named::("SELECT SUM(x) FROM foo WHERE x > :x", - &[(":x", &0i32)], - |r| r.get(0)) - .unwrap()); - } - - #[test] - fn test_stmt_execute_named() { - let db = Connection::open_in_memory().unwrap(); - let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \ - INTEGER)"; - db.execute_batch(sql).unwrap(); - - let mut stmt = db.prepare("INSERT INTO test (name) VALUES (:name)").unwrap(); - stmt.execute_named(&[(":name", &"one")]).unwrap(); - - assert_eq!(1i32, - db.query_row_named::("SELECT COUNT(*) FROM test WHERE name = :name", - &[(":name", &"one")], - |r| r.get(0)) - .unwrap()); - } - - #[test] - fn test_query_named() { - let db = Connection::open_in_memory().unwrap(); - let sql = r#" - CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER); - INSERT INTO test(id, name) VALUES (1, "one"); - "#; - db.execute_batch(sql).unwrap(); - - let mut stmt = db.prepare("SELECT id FROM test where name = :name").unwrap(); - let mut rows = stmt.query_named(&[(":name", &"one")]).unwrap(); - - let id: i32 = rows.next().unwrap().unwrap().get(0); - assert_eq!(1, id); - } - - #[test] - fn test_query_map_named() { - let db = Connection::open_in_memory().unwrap(); - let sql = r#" - CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER); - INSERT INTO test(id, name) VALUES (1, "one"); - "#; - db.execute_batch(sql).unwrap(); - - let mut stmt = db.prepare("SELECT id FROM test where name = :name").unwrap(); - let mut rows = stmt.query_map_named(&[(":name", &"one")], |row| { - let id: i32 = row.get(0); - 2 * id - }) - .unwrap(); - - let doubled_id: i32 = rows.next().unwrap().unwrap(); - assert_eq!(2, doubled_id); - } - - #[test] - fn test_query_and_then_named() { - - let db = Connection::open_in_memory().unwrap(); - let sql = r#" - CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER); - INSERT INTO test(id, name) VALUES (1, "one"); - INSERT INTO test(id, name) VALUES (2, "one"); - "#; - db.execute_batch(sql).unwrap(); - - let mut stmt = db.prepare("SELECT id FROM test where name = :name ORDER BY id ASC") - .unwrap(); - let mut rows = stmt.query_and_then_named(&[(":name", &"one")], |row| { - let id: i32 = row.get(0); - if id == 1 { - Ok(id) - } else { - Err(Error::SqliteSingleThreadedMode) - } - }) - .unwrap(); - - // first row should be Ok - let doubled_id: i32 = rows.next().unwrap().unwrap(); - assert_eq!(1, doubled_id); - - // second row should be Err - match rows.next().unwrap() { - Ok(_) => panic!("invalid Ok"), - Err(Error::SqliteSingleThreadedMode) => (), - Err(_) => panic!("invalid Err"), - } - } - - #[test] - fn test_unbound_parameters_are_null() { - let db = Connection::open_in_memory().unwrap(); - let sql = "CREATE TABLE test (x TEXT, y TEXT)"; - db.execute_batch(sql).unwrap(); - - let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)").unwrap(); - stmt.execute_named(&[(":x", &"one")]).unwrap(); - - let result: Option = - db.query_row("SELECT y FROM test WHERE x = 'one'", &[], |row| row.get(0)) - .unwrap(); - assert!(result.is_none()); - } - - #[test] - fn test_unbound_parameters_are_reused() { - let db = Connection::open_in_memory().unwrap(); - let sql = "CREATE TABLE test (x TEXT, y TEXT)"; - db.execute_batch(sql).unwrap(); - - let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)").unwrap(); - stmt.execute_named(&[(":x", &"one")]).unwrap(); - stmt.execute_named(&[(":y", &"two")]).unwrap(); - - let result: String = - db.query_row("SELECT x FROM test WHERE y = 'two'", &[], |row| row.get(0)) - .unwrap(); - assert_eq!(result, "one"); - } -} diff --git a/src/statement.rs b/src/statement.rs new file mode 100644 index 0000000..5e5b94d --- /dev/null +++ b/src/statement.rs @@ -0,0 +1,779 @@ +use std::{convert, fmt, mem, ptr, result, str}; +use std::ffi::CStr; +use std::os::raw::{c_char, c_int, c_void}; +use std::slice::from_raw_parts; + +use super::ffi; +use super::{Connection, RawStatement, Result, Error, ValueRef, Row, Rows, AndThenRows, MappedRows}; +use super::str_to_cstring; +use types::{ToSql, ToSqlOutput}; + +/// A prepared statement. +pub struct Statement<'conn> { + conn: &'conn Connection, + stmt: RawStatement, +} + +impl<'conn> Statement<'conn> { + /// Get all the column names in the result set of the prepared statement. + pub fn column_names(&self) -> Vec<&str> { + let n = self.column_count(); + let mut cols = Vec::with_capacity(n as usize); + for i in 0..n { + let slice = self.stmt.column_name(i); + let s = str::from_utf8(slice.to_bytes()).unwrap(); + cols.push(s); + } + cols + } + + /// Return the number of columns in the result set returned by the prepared statement. + pub fn column_count(&self) -> i32 { + self.stmt.column_count() + } + + /// Returns the column index in the result set for a given column name. + /// + /// If there is no AS clause then the name of the column is unspecified and may change from one + /// release of SQLite to the next. + /// + /// # Failure + /// + /// Will return an `Error::InvalidColumnName` when there is no column with the specified `name`. + pub fn column_index(&self, name: &str) -> Result { + let bytes = name.as_bytes(); + let n = self.column_count(); + for i in 0..n { + if bytes == self.stmt.column_name(i).to_bytes() { + return Ok(i); + } + } + Err(Error::InvalidColumnName(String::from(name))) + } + + /// Execute the prepared statement. + /// + /// On success, returns the number of rows that were changed or inserted or deleted (via + /// `sqlite3_changes`). + /// + /// ## Example + /// + /// ```rust,no_run + /// # use rusqlite::{Connection, Result}; + /// fn update_rows(conn: &Connection) -> Result<()> { + /// let mut stmt = try!(conn.prepare("UPDATE foo SET bar = 'baz' WHERE qux = ?")); + /// + /// try!(stmt.execute(&[&1i32])); + /// try!(stmt.execute(&[&2i32])); + /// + /// Ok(()) + /// } + /// ``` + /// + /// # Failure + /// + /// Will return `Err` if binding parameters fails, the executed statement returns rows (in + /// which case `query` should be used instead), or the underling SQLite call fails. + pub fn execute(&mut self, params: &[&ToSql]) -> Result { + try!(self.bind_parameters(params)); + self.execute_with_bound_parameters() + } + + /// Execute the prepared statement with named parameter(s). If any parameters + /// that were in the prepared statement are not included in `params`, they + /// will continue to use the most-recently bound value from a previous call + /// to `execute_named`, or `NULL` if they have never been bound. + /// + /// On success, returns the number of rows that were changed or inserted or deleted (via + /// `sqlite3_changes`). + /// + /// ## Example + /// + /// ```rust,no_run + /// # use rusqlite::{Connection, Result}; + /// fn insert(conn: &Connection) -> Result { + /// let mut stmt = try!(conn.prepare("INSERT INTO test (name) VALUES (:name)")); + /// stmt.execute_named(&[(":name", &"one")]) + /// } + /// ``` + /// + /// # Failure + /// + /// Will return `Err` if binding parameters fails, the executed statement returns rows (in + /// which case `query` should be used instead), or the underling SQLite call fails. + pub fn execute_named(&mut self, params: &[(&str, &ToSql)]) -> Result { + try!(self.bind_parameters_named(params)); + self.execute_with_bound_parameters() + } + + /// Execute an INSERT and return the ROWID. + /// + /// # Note + /// + /// This function is a convenience wrapper around `execute()` intended for queries that + /// insert a single item. It is possible to misuse this function in a way that it cannot + /// detect, such as by calling it on a statement which _updates_ a single item rather than + /// inserting one. Please don't do that. + /// + /// # Failure + /// + /// Will return `Err` if no row is inserted or many rows are inserted. + pub fn insert(&mut self, params: &[&ToSql]) -> Result { + let changes = try!(self.execute(params)); + match changes { + 1 => Ok(self.conn.last_insert_rowid()), + _ => Err(Error::StatementChangedRows(changes)), + } + } + + /// Execute the prepared statement, returning a handle to the resulting rows. + /// + /// Due to lifetime restricts, the rows handle returned by `query` does not + /// implement the `Iterator` trait. Consider using `query_map` or `query_and_then` + /// instead, which do. + /// + /// ## Example + /// + /// ```rust,no_run + /// # use rusqlite::{Connection, Result}; + /// fn get_names(conn: &Connection) -> Result> { + /// let mut stmt = try!(conn.prepare("SELECT name FROM people")); + /// let mut rows = try!(stmt.query(&[])); + /// + /// let mut names = Vec::new(); + /// while let Some(result_row) = rows.next() { + /// let row = try!(result_row); + /// names.push(row.get(0)); + /// } + /// + /// Ok(names) + /// } + /// ``` + /// + /// ## Failure + /// + /// Will return `Err` if binding parameters fails. + pub fn query<'a>(&'a mut self, params: &[&ToSql]) -> Result> { + try!(self.bind_parameters(params)); + Ok(Rows::new(self)) + } + + /// Execute the prepared statement with named parameter(s), returning a handle for the + /// resulting rows. If any parameters that were in the prepared statement are not included in + /// `params`, they will continue to use the most-recently bound value from a previous call to + /// `query_named`, or `NULL` if they have never been bound. + /// + /// ## Example + /// + /// ```rust,no_run + /// # use rusqlite::{Connection, Result}; + /// fn query(conn: &Connection) -> Result<()> { + /// let mut stmt = try!(conn.prepare("SELECT * FROM test where name = :name")); + /// let mut rows = try!(stmt.query_named(&[(":name", &"one")])); + /// while let Some(row) = rows.next() { + /// // ... + /// } + /// Ok(()) + /// } + /// ``` + /// + /// # Failure + /// + /// Will return `Err` if binding parameters fails. + pub fn query_named<'a>(&'a mut self, params: &[(&str, &ToSql)]) -> Result> { + try!(self.bind_parameters_named(params)); + Ok(Rows::new(self)) + } + + /// Executes the prepared statement and maps a function over the resulting rows, returning + /// an iterator over the mapped function results. + /// + /// ## Example + /// + /// ```rust,no_run + /// # use rusqlite::{Connection, Result}; + /// fn get_names(conn: &Connection) -> Result> { + /// let mut stmt = try!(conn.prepare("SELECT name FROM people")); + /// let rows = try!(stmt.query_map(&[], |row| row.get(0))); + /// + /// let mut names = Vec::new(); + /// for name_result in rows { + /// names.push(try!(name_result)); + /// } + /// + /// Ok(names) + /// } + /// ``` + /// + /// ## Failure + /// + /// Will return `Err` if binding parameters fails. + pub fn query_map<'a, T, F>(&'a mut self, params: &[&ToSql], f: F) -> Result> + where F: FnMut(&Row) -> T + { + let row_iter = try!(self.query(params)); + + Ok(MappedRows { + rows: row_iter, + map: f, + }) + } + + /// Execute the prepared statement with named parameter(s), returning an iterator over the + /// result of calling the mapping function over the query's rows. If any parameters that were + /// in the prepared statement are not included in `params`, they will continue to use the + /// most-recently bound value from a previous call to `query_named`, or `NULL` if they have + /// never been bound. + /// + /// ## Example + /// + /// ```rust,no_run + /// # use rusqlite::{Connection, Result}; + /// fn get_names(conn: &Connection) -> Result> { + /// let mut stmt = try!(conn.prepare("SELECT name FROM people WHERE id = :id")); + /// let rows = try!(stmt.query_map_named(&[(":id", &"one")], |row| row.get(0))); + /// + /// let mut names = Vec::new(); + /// for name_result in rows { + /// names.push(try!(name_result)); + /// } + /// + /// Ok(names) + /// } + /// ``` + /// + /// ## Failure + /// + /// Will return `Err` if binding parameters fails. + pub fn query_map_named<'a, T, F>(&'a mut self, + params: &[(&str, &ToSql)], + f: F) + -> Result> + where F: FnMut(&Row) -> T + { + let rows = try!(self.query_named(params)); + Ok(MappedRows { + rows: rows, + map: f, + }) + } + + /// Executes the prepared statement and maps a function over the resulting + /// rows, where the function returns a `Result` with `Error` type implementing + /// `std::convert::From` (so errors can be unified). + /// + /// # Failure + /// + /// Will return `Err` if binding parameters fails. + pub fn query_and_then<'a, T, E, F>(&'a mut self, + params: &[&ToSql], + f: F) + -> Result> + where E: convert::From, + F: FnMut(&Row) -> result::Result + { + let row_iter = try!(self.query(params)); + + Ok(AndThenRows { + rows: row_iter, + map: f, + }) + } + + /// Execute the prepared statement with named parameter(s), returning an iterator over the + /// result of calling the mapping function over the query's rows. If any parameters that were + /// in the prepared statement are not included in `params`, they will continue to use the + /// most-recently bound value from a previous call to `query_named`, or `NULL` if they have + /// never been bound. + /// + /// ## Example + /// + /// ```rust,no_run + /// # use rusqlite::{Connection, Result}; + /// struct Person { name: String }; + /// + /// fn name_to_person(name: String) -> Result { + /// // ... check for valid name + /// Ok(Person{ name: name }) + /// } + /// + /// fn get_names(conn: &Connection) -> Result> { + /// let mut stmt = try!(conn.prepare("SELECT name FROM people WHERE id = :id")); + /// let rows = try!(stmt.query_and_then_named(&[(":id", &"one")], |row| { + /// name_to_person(row.get(0)) + /// })); + /// + /// let mut persons = Vec::new(); + /// for person_result in rows { + /// persons.push(try!(person_result)); + /// } + /// + /// Ok(persons) + /// } + /// ``` + /// + /// ## Failure + /// + /// Will return `Err` if binding parameters fails. + pub fn query_and_then_named<'a, T, E, F>(&'a mut self, + params: &[(&str, &ToSql)], + f: F) + -> Result> + where E: convert::From, + F: FnMut(&Row) -> result::Result + { + let rows = try!(self.query_named(params)); + Ok(AndThenRows { + rows: rows, + map: f, + }) + } + + /// Return `true` if a query in the SQL statement it executes returns one or more rows + /// and `false` if the SQL returns an empty set. + pub fn exists(&mut self, params: &[&ToSql]) -> Result { + let mut rows = try!(self.query(params)); + let exists = { + match rows.next() { + Some(_) => true, + None => false, + } + }; + Ok(exists) + } + + /// Convenience method to execute a query that is expected to return a single row. + /// + /// If the query returns more than one row, all rows except the first are ignored. + /// + /// # Failure + /// + /// Will return `Err` if the underlying SQLite call fails. + pub fn query_row(&mut self, params: &[&ToSql], f: F) -> Result + where F: FnOnce(&Row) -> T + { + let mut rows = try!(self.query(params)); + + rows.get_expected_row().map(|r| f(&r)) + } + + /// Consumes the statement. + /// + /// Functionally equivalent to the `Drop` implementation, but allows callers to see any errors + /// that occur. + /// + /// # Failure + /// + /// Will return `Err` if the underlying SQLite call fails. + pub fn finalize(mut self) -> Result<()> { + self.finalize_() + } + + /// Return the index of an SQL parameter given its name. + /// + /// # Failure + /// + /// Will return Err if `name` is invalid. Will return Ok(None) if the name + /// is valid but not a bound parameter of this statement. + pub fn parameter_index(&self, name: &str) -> Result> { + let c_name = try!(str_to_cstring(name)); + Ok(self.stmt.bind_parameter_index(&c_name)) + } + + fn bind_parameters(&mut self, params: &[&ToSql]) -> Result<()> { + assert!(params.len() as c_int == self.stmt.bind_parameter_count(), + "incorrect number of parameters to query(): expected {}, got {}", + self.stmt.bind_parameter_count(), + params.len()); + + for (i, p) in params.iter().enumerate() { + try!(self.bind_parameter(*p, (i + 1) as c_int)); + } + + Ok(()) + } + + fn bind_parameters_named(&mut self, params: &[(&str, &ToSql)]) -> Result<()> { + for &(name, value) in params { + if let Some(i) = try!(self.parameter_index(name)) { + try!(self.bind_parameter(value, i)); + } else { + return Err(Error::InvalidParameterName(name.into())); + } + } + Ok(()) + } + + fn bind_parameter(&self, param: &ToSql, col: c_int) -> Result<()> { + let value = try!(param.to_sql()); + + let ptr = unsafe { self.stmt.ptr() }; + let value = match value { + ToSqlOutput::Borrowed(v) => v, + ToSqlOutput::Owned(ref v) => ValueRef::from(v), + + #[cfg(feature = "blob")] + ToSqlOutput::ZeroBlob(len) => { + return self.conn + .decode_result(unsafe { ffi::sqlite3_bind_zeroblob(ptr, col, len) }); + } + }; + self.conn.decode_result(match value { + ValueRef::Null => unsafe { ffi::sqlite3_bind_null(ptr, col) }, + ValueRef::Integer(i) => unsafe { ffi::sqlite3_bind_int64(ptr, col, i) }, + ValueRef::Real(r) => unsafe { ffi::sqlite3_bind_double(ptr, col, r) }, + ValueRef::Text(s) => unsafe { + let length = s.len(); + if length > ::std::i32::MAX as usize { + ffi::SQLITE_TOOBIG + } else { + let c_str = try!(str_to_cstring(s)); + let destructor = if length > 0 { + ffi::SQLITE_TRANSIENT() + } else { + ffi::SQLITE_STATIC() + }; + ffi::sqlite3_bind_text(ptr, col, c_str.as_ptr(), length as c_int, destructor) + } + }, + ValueRef::Blob(b) => unsafe { + let length = b.len(); + if length > ::std::i32::MAX as usize { + ffi::SQLITE_TOOBIG + } else if length == 0 { + ffi::sqlite3_bind_zeroblob(ptr, col, 0) + } else { + ffi::sqlite3_bind_blob(ptr, + col, + b.as_ptr() as *const c_void, + length as c_int, + ffi::SQLITE_TRANSIENT()) + } + }, + }) + } + + fn execute_with_bound_parameters(&mut self) -> Result { + let r = self.stmt.step(); + self.stmt.reset(); + match r { + ffi::SQLITE_DONE => { + if self.column_count() == 0 { + Ok(self.conn.changes()) + } else { + Err(Error::ExecuteReturnedResults) + } + } + ffi::SQLITE_ROW => Err(Error::ExecuteReturnedResults), + _ => Err(self.conn.decode_result(r).unwrap_err()), + } + } + + fn finalize_(&mut self) -> Result<()> { + let mut stmt = RawStatement::new(ptr::null_mut()); + mem::swap(&mut stmt, &mut self.stmt); + self.conn.decode_result(stmt.finalize()) + } +} + +impl<'conn> Into for Statement<'conn> { + fn into(mut self) -> RawStatement { + let mut stmt = RawStatement::new(ptr::null_mut()); + mem::swap(&mut stmt, &mut self.stmt); + stmt + } +} + +impl<'conn> fmt::Debug for Statement<'conn> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let sql = str::from_utf8(self.stmt.sql().to_bytes()); + f.debug_struct("Statement") + .field("conn", self.conn) + .field("stmt", &self.stmt) + .field("sql", &sql) + .finish() + } +} + +impl<'conn> Drop for Statement<'conn> { + #[allow(unused_must_use)] + fn drop(&mut self) { + self.finalize_(); + } +} + +// TODO: This trait lets us have "pub(crate)" visibility on some Statement methods. Remove this +// once pub(crate) is stable. +pub trait StatementCrateImpl<'conn> { + fn new(conn: &'conn Connection, stmt: RawStatement) -> Self; + fn value_ref(&self, col: c_int) -> ValueRef; + fn step(&self) -> Result; + fn reset(&self) -> c_int; +} + +impl<'conn> StatementCrateImpl<'conn> for Statement<'conn> { + fn new(conn: &Connection, stmt: RawStatement) -> Statement { + Statement { + conn: conn, + stmt: stmt, + } + } + + fn value_ref(&self, col: c_int) -> ValueRef { + let raw = unsafe { self.stmt.ptr() }; + + match self.stmt.column_type(col) { + ffi::SQLITE_NULL => ValueRef::Null, + ffi::SQLITE_INTEGER => { + ValueRef::Integer(unsafe { ffi::sqlite3_column_int64(raw, col) }) + } + ffi::SQLITE_FLOAT => ValueRef::Real(unsafe { ffi::sqlite3_column_double(raw, col) }), + ffi::SQLITE_TEXT => { + let s = unsafe { + let text = ffi::sqlite3_column_text(raw, col); + assert!(!text.is_null(), + "unexpected SQLITE_TEXT column type with NULL data"); + CStr::from_ptr(text as *const c_char) + }; + + // sqlite3_column_text returns UTF8 data, so our unwrap here should be fine. + let s = s.to_str().expect("sqlite3_column_text returned invalid UTF-8"); + ValueRef::Text(s) + } + ffi::SQLITE_BLOB => { + let (blob, len) = unsafe { + (ffi::sqlite3_column_blob(raw, col), ffi::sqlite3_column_bytes(raw, col)) + }; + + assert!(len >= 0, + "unexpected negative return from sqlite3_column_bytes"); + if len > 0 { + assert!(!blob.is_null(), + "unexpected SQLITE_BLOB column type with NULL data"); + ValueRef::Blob(unsafe { from_raw_parts(blob as *const u8, len as usize) }) + } else { + // The return value from sqlite3_column_blob() for a zero-length BLOB + // is a NULL pointer. + ValueRef::Blob(&[]) + } + } + _ => unreachable!("sqlite3_column_type returned invalid value"), + } + } + + fn step(&self) -> Result { + match self.stmt.step() { + ffi::SQLITE_ROW => Ok(true), + ffi::SQLITE_DONE => Ok(false), + code => Err(self.conn.decode_result(code).unwrap_err()), + } + } + + fn reset(&self) -> c_int { + self.stmt.reset() + } +} + +#[cfg(test)] +mod test { + use {Connection, Error, Result}; + + #[test] + fn test_execute_named() { + let db = Connection::open_in_memory().unwrap(); + db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap(); + + assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)]).unwrap(), + 1); + assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)]).unwrap(), + 1); + + assert_eq!(3i32, + db.query_row_named::("SELECT SUM(x) FROM foo WHERE x > :x", + &[(":x", &0i32)], + |r| r.get(0)) + .unwrap()); + } + + #[test] + fn test_stmt_execute_named() { + let db = Connection::open_in_memory().unwrap(); + let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \ + INTEGER)"; + db.execute_batch(sql).unwrap(); + + let mut stmt = db.prepare("INSERT INTO test (name) VALUES (:name)").unwrap(); + stmt.execute_named(&[(":name", &"one")]).unwrap(); + + assert_eq!(1i32, + db.query_row_named::("SELECT COUNT(*) FROM test WHERE name = :name", + &[(":name", &"one")], + |r| r.get(0)) + .unwrap()); + } + + #[test] + fn test_query_named() { + let db = Connection::open_in_memory().unwrap(); + let sql = r#" + CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER); + INSERT INTO test(id, name) VALUES (1, "one"); + "#; + db.execute_batch(sql).unwrap(); + + let mut stmt = db.prepare("SELECT id FROM test where name = :name").unwrap(); + let mut rows = stmt.query_named(&[(":name", &"one")]).unwrap(); + + let id: i32 = rows.next().unwrap().unwrap().get(0); + assert_eq!(1, id); + } + + #[test] + fn test_query_map_named() { + let db = Connection::open_in_memory().unwrap(); + let sql = r#" + CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER); + INSERT INTO test(id, name) VALUES (1, "one"); + "#; + db.execute_batch(sql).unwrap(); + + let mut stmt = db.prepare("SELECT id FROM test where name = :name").unwrap(); + let mut rows = stmt.query_map_named(&[(":name", &"one")], |row| { + let id: i32 = row.get(0); + 2 * id + }) + .unwrap(); + + let doubled_id: i32 = rows.next().unwrap().unwrap(); + assert_eq!(2, doubled_id); + } + + #[test] + fn test_query_and_then_named() { + + let db = Connection::open_in_memory().unwrap(); + let sql = r#" + CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER); + INSERT INTO test(id, name) VALUES (1, "one"); + INSERT INTO test(id, name) VALUES (2, "one"); + "#; + db.execute_batch(sql).unwrap(); + + let mut stmt = db.prepare("SELECT id FROM test where name = :name ORDER BY id ASC") + .unwrap(); + let mut rows = stmt.query_and_then_named(&[(":name", &"one")], |row| { + let id: i32 = row.get(0); + if id == 1 { + Ok(id) + } else { + Err(Error::SqliteSingleThreadedMode) + } + }) + .unwrap(); + + // first row should be Ok + let doubled_id: i32 = rows.next().unwrap().unwrap(); + assert_eq!(1, doubled_id); + + // second row should be Err + match rows.next().unwrap() { + Ok(_) => panic!("invalid Ok"), + Err(Error::SqliteSingleThreadedMode) => (), + Err(_) => panic!("invalid Err"), + } + } + + #[test] + fn test_unbound_parameters_are_null() { + let db = Connection::open_in_memory().unwrap(); + let sql = "CREATE TABLE test (x TEXT, y TEXT)"; + db.execute_batch(sql).unwrap(); + + let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)").unwrap(); + stmt.execute_named(&[(":x", &"one")]).unwrap(); + + let result: Option = + db.query_row("SELECT y FROM test WHERE x = 'one'", &[], |row| row.get(0)) + .unwrap(); + assert!(result.is_none()); + } + + #[test] + fn test_unbound_parameters_are_reused() { + let db = Connection::open_in_memory().unwrap(); + let sql = "CREATE TABLE test (x TEXT, y TEXT)"; + db.execute_batch(sql).unwrap(); + + let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)").unwrap(); + stmt.execute_named(&[(":x", &"one")]).unwrap(); + stmt.execute_named(&[(":y", &"two")]).unwrap(); + + let result: String = + db.query_row("SELECT x FROM test WHERE y = 'two'", &[], |row| row.get(0)) + .unwrap(); + assert_eq!(result, "one"); + } + + #[test] + fn test_insert() { + let db = Connection::open_in_memory().unwrap(); + db.execute_batch("CREATE TABLE foo(x INTEGER UNIQUE)").unwrap(); + let mut stmt = db.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?)").unwrap(); + assert_eq!(stmt.insert(&[&1i32]).unwrap(), 1); + assert_eq!(stmt.insert(&[&2i32]).unwrap(), 2); + match stmt.insert(&[&1i32]).unwrap_err() { + Error::StatementChangedRows(0) => (), + err => panic!("Unexpected error {}", err), + } + let mut multi = db.prepare("INSERT INTO foo (x) SELECT 3 UNION ALL SELECT 4").unwrap(); + match multi.insert(&[]).unwrap_err() { + Error::StatementChangedRows(2) => (), + err => panic!("Unexpected error {}", err), + } + } + + #[test] + fn test_insert_different_tables() { + // Test for https://github.com/jgallagher/rusqlite/issues/171 + let db = Connection::open_in_memory().unwrap(); + db.execute_batch(r" + CREATE TABLE foo(x INTEGER); + CREATE TABLE bar(x INTEGER); + ") + .unwrap(); + + assert_eq!(db.prepare("INSERT INTO foo VALUES (10)").unwrap().insert(&[]).unwrap(), + 1); + assert_eq!(db.prepare("INSERT INTO bar VALUES (10)").unwrap().insert(&[]).unwrap(), + 1); + } + + #[test] + fn test_exists() { + let db = Connection::open_in_memory().unwrap(); + let sql = "BEGIN; + CREATE TABLE foo(x INTEGER); + INSERT INTO foo VALUES(1); + INSERT INTO foo VALUES(2); + END;"; + db.execute_batch(sql).unwrap(); + let mut stmt = db.prepare("SELECT 1 FROM foo WHERE x = ?").unwrap(); + assert!(stmt.exists(&[&1i32]).unwrap()); + assert!(stmt.exists(&[&2i32]).unwrap()); + assert!(!stmt.exists(&[&0i32]).unwrap()); + } + + #[test] + fn test_query_row() { + let db = Connection::open_in_memory().unwrap(); + let sql = "BEGIN; + CREATE TABLE foo(x INTEGER, y INTEGER); + INSERT INTO foo VALUES(1, 3); + INSERT INTO foo VALUES(2, 4); + END;"; + db.execute_batch(sql).unwrap(); + let mut stmt = db.prepare("SELECT y FROM foo WHERE x = ?").unwrap(); + let y: Result = stmt.query_row(&[&1i32], |r| r.get(0)); + assert_eq!(3i64, y.unwrap()); + } +}