From 38c9a4a159582740aa540be30eea82ac542b18f8 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Wed, 8 Mar 2017 10:48:14 -0500 Subject: [PATCH] Reorganize: Extract Statement and its impl into its own module. --- src/cache.rs | 1 + src/convenient.rs | 3 +- src/lib.rs | 346 ++-------------------------------------- src/named_params.rs | 3 +- src/statement.rs | 376 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 391 insertions(+), 338 deletions(-) create mode 100644 src/statement.rs 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 index 4740aa2..de5c25c 100644 --- a/src/convenient.rs +++ b/src/convenient.rs @@ -1,5 +1,6 @@ use {Error, Result, Row, Statement}; use types::ToSql; +use statement::StatementCrateImpl; impl<'conn> Statement<'conn> { /// Execute an INSERT and return the ROWID. @@ -17,7 +18,7 @@ impl<'conn> Statement<'conn> { pub fn insert(&mut self, params: &[&ToSql]) -> Result { let changes = try!(self.execute(params)); match changes { - 1 => Ok(self.conn.last_insert_rowid()), + 1 => Ok(self.last_insert_rowid()), _ => Err(Error::StatementChangedRows(changes)), } } diff --git a/src/lib.rs b/src/lib.rs index 87c752f..92d4f52 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}; @@ -104,6 +107,7 @@ mod named_params; mod error; mod convenient; mod raw_statement; +mod statement; #[cfg(feature = "load_extension")] mod load_extension_guard; #[cfg(feature = "trace")] @@ -823,295 +827,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 +890,7 @@ impl<'stmt> Rows<'stmt> { fn reset(&mut self) { if let Some(stmt) = self.stmt.take() { - stmt.stmt.reset(); + stmt.reset(); } } @@ -1190,7 +905,7 @@ 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() { + self.stmt.and_then(|stmt| match stmt.step() { ffi::SQLITE_ROW => { Some(Ok(Row { stmt: stmt, @@ -1203,7 +918,7 @@ impl<'stmt> Rows<'stmt> { } code => { self.reset(); - Some(Err(stmt.conn.decode_result(code).unwrap_err())) + Some(Err(stmt.decode_result(code).unwrap_err())) } }) } @@ -1253,7 +968,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 +1009,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 index d6f8382..81b6bd7 100644 --- a/src/named_params.rs +++ b/src/named_params.rs @@ -1,6 +1,7 @@ use std::convert; use std::result; use std::os::raw::c_int; +use statement::StatementCrateImpl; use {Result, Error, Connection, Statement, MappedRows, AndThenRows, Rows, Row, str_to_cstring}; use types::ToSql; @@ -56,7 +57,7 @@ impl<'conn> Statement<'conn> { /// 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)) + Ok(self.bind_parameter_index(&c_name)) } /// Execute the prepared statement with named parameter(s). If any parameters diff --git a/src/statement.rs b/src/statement.rs new file mode 100644 index 0000000..620f924 --- /dev/null +++ b/src/statement.rs @@ -0,0 +1,376 @@ +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_() + } + + /// 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_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_(); + } +} + +// 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 execute_(&mut self) -> Result; + fn bind_parameter(&self, param: &ToSql, col: c_int) -> Result<()>; + fn bind_parameter_index(&self, name: &CStr) -> Option; + fn last_insert_rowid(&self) -> i64; + fn value_ref(&self, col: c_int) -> ValueRef; + fn step(&self) -> c_int; + fn reset(&self) -> c_int; + fn decode_result(&self, code: c_int) -> Result<()>; +} + +impl<'conn> StatementCrateImpl<'conn> for Statement<'conn> { + fn new(conn: &Connection, stmt: RawStatement) -> Statement { + Statement { + conn: conn, + stmt: stmt, + } + } + + 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()), + } + } + + 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_parameter_index(&self, name: &CStr) -> Option { + self.stmt.bind_parameter_index(name) + } + + fn last_insert_rowid(&self) -> i64 { + self.conn.last_insert_rowid() + } + + 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) -> i32 { + self.stmt.step() + } + + fn reset(&self) -> c_int { + self.stmt.reset() + } + + fn decode_result(&self, code: c_int) -> Result<()> { + self.conn.decode_result(code) + } +}