mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-30 06:01:36 +08:00
Merge remote-tracking branch 'jgallagher/master' into vtab
This commit is contained in:
commit
ef6fcc8976
14
Changelog.md
14
Changelog.md
@ -1,5 +1,15 @@
|
|||||||
# Version UPCOMING (...)
|
# Version UPCOMING (...)
|
||||||
|
|
||||||
|
* BREAKING CHANGE: `Rows` no longer implements `Iterator`. It still has a `next()` method, but
|
||||||
|
the lifetime of the returned `Row` is now tied to the lifetime of the vending `Rows` object.
|
||||||
|
This behavior is more correct. Previously there were runtime checks to prevent misuse, but
|
||||||
|
other changes in this release to reset statements as soon as possible introduced yet another
|
||||||
|
hazard related to the lack of these lifetime connections. We were already recommending the
|
||||||
|
use of `query_map` and `query_and_then` over raw `query`; both of theose still return handles
|
||||||
|
that implement `Iterator`.
|
||||||
|
* BREAKING CHANGE: `Transaction::savepoint()` now returns a `Savepoint` instead of another
|
||||||
|
`Transaction`. Unlike `Transaction`, `Savepoint`s can be rolled back while keeping the current
|
||||||
|
savepoint active.
|
||||||
* BREAKING CHANGE: Creating transactions from a `Connection` or savepoints from a `Transaction`
|
* BREAKING CHANGE: Creating transactions from a `Connection` or savepoints from a `Transaction`
|
||||||
now take `&mut self` instead of `&self` to correctly represent that transactions within a
|
now take `&mut self` instead of `&self` to correctly represent that transactions within a
|
||||||
connection are inherently nested. While a transaction is alive, the parent connection or
|
connection are inherently nested. While a transaction is alive, the parent connection or
|
||||||
@ -7,13 +17,11 @@
|
|||||||
access to `Connection`'s methods via the `Transaction` itself.
|
access to `Connection`'s methods via the `Transaction` itself.
|
||||||
* BREAKING CHANGE: `Transaction::set_commit` and `Transaction::set_rollback` have been replaced
|
* BREAKING CHANGE: `Transaction::set_commit` and `Transaction::set_rollback` have been replaced
|
||||||
by `Transaction::set_drop_behavior`.
|
by `Transaction::set_drop_behavior`.
|
||||||
* BREAKING CHANGE: `Transaction::savepoint()` now returns a `Savepoint` instead of another
|
|
||||||
`Transaction`. Unlike `Transaction`, `Savepoint`s can be rolled back while keeping the current
|
|
||||||
savepoint active.
|
|
||||||
* Adds `Connection::prepare_cached`. `Connection` now keeps an internal cache of any statements
|
* Adds `Connection::prepare_cached`. `Connection` now keeps an internal cache of any statements
|
||||||
prepared via this method. The size of this cache defaults to 16 (`prepare_cached` will always
|
prepared via this method. The size of this cache defaults to 16 (`prepare_cached` will always
|
||||||
work but may re-prepare statements if more are prepared than the cache holds), and can be
|
work but may re-prepare statements if more are prepared than the cache holds), and can be
|
||||||
controlled via `Connection::set_prepared_statement_cache_capacity`.
|
controlled via `Connection::set_prepared_statement_cache_capacity`.
|
||||||
|
* Adds `query_map_named` and `query_and_then_named` to `Statement`.
|
||||||
* Adds `insert` convenience method to `Statement` which returns the row ID of an inserted row.
|
* Adds `insert` convenience method to `Statement` which returns the row ID of an inserted row.
|
||||||
* Adds `exists` convenience method returning whether a query finds one or more rows.
|
* Adds `exists` convenience method returning whether a query finds one or more rows.
|
||||||
* Adds support for serializing types from the `serde_json` crate. Requires the `serde_json` feature.
|
* Adds support for serializing types from the `serde_json` crate. Requires the `serde_json` feature.
|
||||||
|
@ -40,10 +40,6 @@ pub enum Error {
|
|||||||
/// did not return any.
|
/// did not return any.
|
||||||
QueryReturnedNoRows,
|
QueryReturnedNoRows,
|
||||||
|
|
||||||
/// Error when trying to access a `Row` after stepping past it. See the discussion on
|
|
||||||
/// the `Rows` type for more details.
|
|
||||||
GetFromStaleRow,
|
|
||||||
|
|
||||||
/// Error when the value of a particular column is requested, but the index is out of range
|
/// Error when the value of a particular column is requested, but the index is out of range
|
||||||
/// for the statement.
|
/// for the statement.
|
||||||
InvalidColumnIndex(c_int),
|
InvalidColumnIndex(c_int),
|
||||||
@ -111,7 +107,6 @@ impl fmt::Display for Error {
|
|||||||
write!(f, "Execute returned results - did you mean to call query?")
|
write!(f, "Execute returned results - did you mean to call query?")
|
||||||
}
|
}
|
||||||
Error::QueryReturnedNoRows => write!(f, "Query returned no rows"),
|
Error::QueryReturnedNoRows => write!(f, "Query returned no rows"),
|
||||||
Error::GetFromStaleRow => write!(f, "Attempted to get a value from a stale row"),
|
|
||||||
Error::InvalidColumnIndex(i) => write!(f, "Invalid column index: {}", i),
|
Error::InvalidColumnIndex(i) => write!(f, "Invalid column index: {}", i),
|
||||||
Error::InvalidColumnName(ref name) => write!(f, "Invalid column name: {}", name),
|
Error::InvalidColumnName(ref name) => write!(f, "Invalid column name: {}", name),
|
||||||
Error::InvalidColumnType(i, t) => write!(f, "Invalid column type {} at index: {}", t, i),
|
Error::InvalidColumnType(i, t) => write!(f, "Invalid column type {} at index: {}", t, i),
|
||||||
@ -145,7 +140,6 @@ impl error::Error for Error {
|
|||||||
"execute returned results - did you mean to call query?"
|
"execute returned results - did you mean to call query?"
|
||||||
}
|
}
|
||||||
Error::QueryReturnedNoRows => "query returned no rows",
|
Error::QueryReturnedNoRows => "query returned no rows",
|
||||||
Error::GetFromStaleRow => "attempted to get a value from a stale row",
|
|
||||||
Error::InvalidColumnIndex(_) => "invalid column index",
|
Error::InvalidColumnIndex(_) => "invalid column index",
|
||||||
Error::InvalidColumnName(_) => "invalid column name",
|
Error::InvalidColumnName(_) => "invalid column name",
|
||||||
Error::InvalidColumnType(_, _) => "invalid column type",
|
Error::InvalidColumnType(_, _) => "invalid column type",
|
||||||
@ -173,7 +167,6 @@ impl error::Error for Error {
|
|||||||
Error::InvalidParameterName(_) |
|
Error::InvalidParameterName(_) |
|
||||||
Error::ExecuteReturnedResults |
|
Error::ExecuteReturnedResults |
|
||||||
Error::QueryReturnedNoRows |
|
Error::QueryReturnedNoRows |
|
||||||
Error::GetFromStaleRow |
|
|
||||||
Error::InvalidColumnIndex(_) |
|
Error::InvalidColumnIndex(_) |
|
||||||
Error::InvalidColumnName(_) |
|
Error::InvalidColumnName(_) |
|
||||||
Error::InvalidColumnType(_, _) |
|
Error::InvalidColumnType(_, _) |
|
||||||
|
181
src/lib.rs
181
src/lib.rs
@ -64,12 +64,12 @@ extern crate lazy_static;
|
|||||||
|
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::convert;
|
use std::convert;
|
||||||
|
use std::marker::PhantomData;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::rc::Rc;
|
use std::cell::RefCell;
|
||||||
use std::cell::{RefCell, Cell};
|
|
||||||
use std::ffi::{CStr, CString};
|
use std::ffi::{CStr, CString};
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::str;
|
use std::str;
|
||||||
@ -769,7 +769,11 @@ impl<'conn> Statement<'conn> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute the prepared statement, returning an iterator over the resulting rows.
|
/// 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
|
/// ## Example
|
||||||
///
|
///
|
||||||
@ -780,7 +784,7 @@ impl<'conn> Statement<'conn> {
|
|||||||
/// let mut rows = try!(stmt.query(&[]));
|
/// let mut rows = try!(stmt.query(&[]));
|
||||||
///
|
///
|
||||||
/// let mut names = Vec::new();
|
/// let mut names = Vec::new();
|
||||||
/// for result_row in rows {
|
/// while let Some(result_row) = rows.next() {
|
||||||
/// let row = try!(result_row);
|
/// let row = try!(result_row);
|
||||||
/// names.push(row.get(0));
|
/// names.push(row.get(0));
|
||||||
/// }
|
/// }
|
||||||
@ -789,7 +793,7 @@ impl<'conn> Statement<'conn> {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// # Failure
|
/// ## Failure
|
||||||
///
|
///
|
||||||
/// Will return `Err` if binding parameters fails.
|
/// Will return `Err` if binding parameters fails.
|
||||||
pub fn query<'a>(&'a mut self, params: &[&ToSql]) -> Result<Rows<'a>> {
|
pub fn query<'a>(&'a mut self, params: &[&ToSql]) -> Result<Rows<'a>> {
|
||||||
@ -797,13 +801,27 @@ impl<'conn> Statement<'conn> {
|
|||||||
Ok(Rows::new(self))
|
Ok(Rows::new(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes the prepared statement and maps a function over the resulting
|
/// Executes the prepared statement and maps a function over the resulting rows, returning
|
||||||
/// rows.
|
/// an iterator over the mapped function results.
|
||||||
///
|
///
|
||||||
/// Unlike the iterator produced by `query`, the returned iterator does not expose the possibility
|
/// ## Example
|
||||||
/// for accessing stale rows.
|
|
||||||
///
|
///
|
||||||
/// # Failure
|
/// ```rust,no_run
|
||||||
|
/// # use rusqlite::{Connection, Result};
|
||||||
|
/// fn get_names(conn: &Connection) -> Result<Vec<String>> {
|
||||||
|
/// 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.
|
/// Will return `Err` if binding parameters fails.
|
||||||
pub fn query_map<'a, T, F>(&'a mut self, params: &[&ToSql], f: F) -> Result<MappedRows<'a, F>>
|
pub fn query_map<'a, T, F>(&'a mut self, params: &[&ToSql], f: F) -> Result<MappedRows<'a, F>>
|
||||||
@ -821,9 +839,6 @@ impl<'conn> Statement<'conn> {
|
|||||||
/// rows, where the function returns a `Result` with `Error` type implementing
|
/// rows, where the function returns a `Result` with `Error` type implementing
|
||||||
/// `std::convert::From<Error>` (so errors can be unified).
|
/// `std::convert::From<Error>` (so errors can be unified).
|
||||||
///
|
///
|
||||||
/// Unlike the iterator produced by `query`, the returned iterator does not expose the possibility
|
|
||||||
/// for accessing stale rows.
|
|
||||||
///
|
|
||||||
/// # Failure
|
/// # Failure
|
||||||
///
|
///
|
||||||
/// Will return `Err` if binding parameters fails.
|
/// Will return `Err` if binding parameters fails.
|
||||||
@ -914,7 +929,8 @@ impl<'stmt, T, F> Iterator for MappedRows<'stmt, F>
|
|||||||
type Item = Result<T>;
|
type Item = Result<T>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Result<T>> {
|
fn next(&mut self) -> Option<Result<T>> {
|
||||||
self.rows.next().map(|row_result| row_result.map(|row| (self.map)(&row)))
|
let map = &mut self.map;
|
||||||
|
self.rows.next().map(|row_result| row_result.map(|row| (map)(&row)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -932,9 +948,10 @@ impl<'stmt, T, E, F> Iterator for AndThenRows<'stmt, F>
|
|||||||
type Item = result::Result<T, E>;
|
type Item = result::Result<T, E>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let map = &mut self.map;
|
||||||
self.rows.next().map(|row_result| {
|
self.rows.next().map(|row_result| {
|
||||||
row_result.map_err(E::from)
|
row_result.map_err(E::from)
|
||||||
.and_then(|row| (self.map)(&row))
|
.and_then(|row| (map)(&row))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -942,52 +959,19 @@ impl<'stmt, T, E, F> Iterator for AndThenRows<'stmt, F>
|
|||||||
/// Old name for `Rows`. `SqliteRows` is deprecated.
|
/// Old name for `Rows`. `SqliteRows` is deprecated.
|
||||||
pub type SqliteRows<'stmt> = Rows<'stmt>;
|
pub type SqliteRows<'stmt> = Rows<'stmt>;
|
||||||
|
|
||||||
/// An iterator over the resulting rows of a query.
|
/// An handle for the resulting rows of a query.
|
||||||
///
|
|
||||||
/// ## Warning
|
|
||||||
///
|
|
||||||
/// Strongly consider using `query_map` or `query_and_then` instead of `query`; the former do not
|
|
||||||
/// suffer from the following problem.
|
|
||||||
///
|
|
||||||
/// Due to the way SQLite returns result rows of a query, it is not safe to attempt to get values
|
|
||||||
/// from a row after it has become stale (i.e., `next()` has been called again on the `Rows`
|
|
||||||
/// iterator). For example:
|
|
||||||
///
|
|
||||||
/// ```rust,no_run
|
|
||||||
/// # use rusqlite::{Connection, Result};
|
|
||||||
/// fn bad_function_will_panic(conn: &Connection) -> Result<i64> {
|
|
||||||
/// let mut stmt = try!(conn.prepare("SELECT id FROM my_table"));
|
|
||||||
/// let mut rows = try!(stmt.query(&[]));
|
|
||||||
///
|
|
||||||
/// let row0 = try!(rows.next().unwrap());
|
|
||||||
/// // row 0 is valid for now...
|
|
||||||
///
|
|
||||||
/// let row1 = try!(rows.next().unwrap());
|
|
||||||
/// // row 0 is now STALE, and row 1 is valid
|
|
||||||
///
|
|
||||||
/// let my_id = row0.get(0); // WILL PANIC because row 0 is stale
|
|
||||||
/// Ok(my_id)
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Please note that this means some of the methods on `Iterator` are not useful, such as `collect`
|
|
||||||
/// (which would result in a collection of rows, only the last of which can safely be used) and
|
|
||||||
/// `min`/`max` (which could return a stale row unless the last row happened to be the min or max,
|
|
||||||
/// respectively).
|
|
||||||
pub struct Rows<'stmt> {
|
pub struct Rows<'stmt> {
|
||||||
stmt: Option<&'stmt Statement<'stmt>>,
|
stmt: Option<&'stmt Statement<'stmt>>,
|
||||||
current_row: Rc<Cell<c_int>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'stmt> Rows<'stmt> {
|
impl<'stmt> Rows<'stmt> {
|
||||||
fn new(stmt: &'stmt Statement<'stmt>) -> Rows<'stmt> {
|
fn new(stmt: &'stmt Statement<'stmt>) -> Rows<'stmt> {
|
||||||
Rows {
|
Rows {
|
||||||
stmt: Some(stmt),
|
stmt: Some(stmt),
|
||||||
current_row: Rc::new(Cell::new(0)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_expected_row(&mut self) -> Result<Row<'stmt>> {
|
fn get_expected_row<'a>(&'a mut self) -> Result<Row<'a, 'stmt>> {
|
||||||
match self.next() {
|
match self.next() {
|
||||||
Some(row) => row,
|
Some(row) => row,
|
||||||
None => Err(Error::QueryReturnedNoRows),
|
None => Err(Error::QueryReturnedNoRows),
|
||||||
@ -999,21 +983,24 @@ impl<'stmt> Rows<'stmt> {
|
|||||||
stmt.stmt.reset();
|
stmt.stmt.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'stmt> Iterator for Rows<'stmt> {
|
/// Attempt to get the next row from the query. Returns `Some(Ok(Row))` if there
|
||||||
type Item = Result<Row<'stmt>>;
|
/// is another row, `Some(Err(...))` if there was an error getting the next
|
||||||
|
/// row, and `None` if all rows have been retrieved.
|
||||||
fn next(&mut self) -> Option<Result<Row<'stmt>>> {
|
///
|
||||||
|
/// ## Note
|
||||||
|
///
|
||||||
|
/// This interface is not compatible with Rust's `Iterator` trait, because the
|
||||||
|
/// lifetime of the returned row is tied to the lifetime of `self`. This is a
|
||||||
|
/// "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<Result<Row<'a, 'stmt>>> {
|
||||||
self.stmt.and_then(|stmt| {
|
self.stmt.and_then(|stmt| {
|
||||||
match stmt.stmt.step() {
|
match stmt.stmt.step() {
|
||||||
ffi::SQLITE_ROW => {
|
ffi::SQLITE_ROW => {
|
||||||
let current_row = self.current_row.get() + 1;
|
|
||||||
self.current_row.set(current_row);
|
|
||||||
Some(Ok(Row {
|
Some(Ok(Row {
|
||||||
stmt: stmt,
|
stmt: stmt,
|
||||||
current_row: self.current_row.clone(),
|
phantom: PhantomData,
|
||||||
row_idx: current_row,
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
ffi::SQLITE_DONE => {
|
ffi::SQLITE_DONE => {
|
||||||
@ -1036,46 +1023,22 @@ impl<'stmt> Drop for Rows<'stmt> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Old name for `Row`. `SqliteRow` is deprecated.
|
/// Old name for `Row`. `SqliteRow` is deprecated.
|
||||||
pub type SqliteRow<'stmt> = Row<'stmt>;
|
pub type SqliteRow<'a, 'stmt> = Row<'a, 'stmt>;
|
||||||
|
|
||||||
/// A single result row of a query.
|
/// A single result row of a query.
|
||||||
pub struct Row<'stmt> {
|
pub struct Row<'a, 'stmt> {
|
||||||
stmt: &'stmt Statement<'stmt>,
|
stmt: &'stmt Statement<'stmt>,
|
||||||
current_row: Rc<Cell<c_int>>,
|
phantom: PhantomData<&'a ()>,
|
||||||
row_idx: c_int,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'stmt> Row<'stmt> {
|
impl<'a, 'stmt> Row<'a, 'stmt> {
|
||||||
/// Get the value of a particular column of the result row.
|
/// Get the value of a particular column of the result row.
|
||||||
///
|
///
|
||||||
/// Note that `Row` can panic at runtime if you use it incorrectly. When you are
|
|
||||||
/// retrieving the rows of a query, a row becomes stale once you have requested the next row,
|
|
||||||
/// and the values can no longer be retrieved. In general (when using looping over the rows,
|
|
||||||
/// for example) this isn't an issue, but it means you cannot do something like this:
|
|
||||||
///
|
|
||||||
/// ```rust,no_run
|
|
||||||
/// # use rusqlite::{Connection, Result};
|
|
||||||
/// fn bad_function_will_panic(conn: &Connection) -> Result<i64> {
|
|
||||||
/// let mut stmt = try!(conn.prepare("SELECT id FROM my_table"));
|
|
||||||
/// let mut rows = try!(stmt.query(&[]));
|
|
||||||
///
|
|
||||||
/// let row0 = try!(rows.next().unwrap());
|
|
||||||
/// // row 0 is value now...
|
|
||||||
///
|
|
||||||
/// let row1 = try!(rows.next().unwrap());
|
|
||||||
/// // row 0 is now STALE, and row 1 is valid
|
|
||||||
///
|
|
||||||
/// let my_id = row0.get(0); // WILL PANIC because row 0 is stale
|
|
||||||
/// Ok(my_id)
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// ## Failure
|
/// ## Failure
|
||||||
///
|
///
|
||||||
/// Panics if the underlying SQLite column type is not a valid type as a source for `T`.
|
/// Panics if the underlying SQLite column type is not a valid type as a source for `T`.
|
||||||
///
|
///
|
||||||
/// Panics if `idx` is outside the range of columns in the returned query or if this row
|
/// Panics if `idx` is outside the range of columns in the returned query.
|
||||||
/// is stale.
|
|
||||||
pub fn get<I: RowIndex, T: FromSql>(&self, idx: I) -> T {
|
pub fn get<I: RowIndex, T: FromSql>(&self, idx: I) -> T {
|
||||||
self.get_checked(idx).unwrap()
|
self.get_checked(idx).unwrap()
|
||||||
}
|
}
|
||||||
@ -1092,12 +1055,7 @@ impl<'stmt> Row<'stmt> {
|
|||||||
///
|
///
|
||||||
/// Returns an `Error::InvalidColumnName` if `idx` is not a valid column name
|
/// Returns an `Error::InvalidColumnName` if `idx` is not a valid column name
|
||||||
/// for this row.
|
/// for this row.
|
||||||
///
|
|
||||||
/// Returns an `Error::GetFromStaleRow` if this row is stale.
|
|
||||||
pub fn get_checked<I: RowIndex, T: FromSql>(&self, idx: I) -> Result<T> {
|
pub fn get_checked<I: RowIndex, T: FromSql>(&self, idx: I) -> Result<T> {
|
||||||
if self.row_idx != self.current_row.get() {
|
|
||||||
return Err(Error::GetFromStaleRow);
|
|
||||||
}
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let idx = try!(idx.idx(self.stmt));
|
let idx = try!(idx.idx(self.stmt));
|
||||||
|
|
||||||
@ -1287,15 +1245,24 @@ mod test {
|
|||||||
|
|
||||||
let mut query = db.prepare("SELECT x FROM foo WHERE x < ? ORDER BY x DESC").unwrap();
|
let mut query = db.prepare("SELECT x FROM foo WHERE x < ? ORDER BY x DESC").unwrap();
|
||||||
{
|
{
|
||||||
let rows = query.query(&[&4i32]).unwrap();
|
let mut rows = query.query(&[&4i32]).unwrap();
|
||||||
let v: Vec<i32> = rows.map(|r| r.unwrap().get(0)).collect();
|
let mut v = Vec::<i32>::new();
|
||||||
|
|
||||||
|
while let Some(row) = rows.next() {
|
||||||
|
v.push(row.unwrap().get(0));
|
||||||
|
}
|
||||||
|
|
||||||
assert_eq!(v, [3i32, 2, 1]);
|
assert_eq!(v, [3i32, 2, 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let rows = query.query(&[&3i32]).unwrap();
|
let mut rows = query.query(&[&3i32]).unwrap();
|
||||||
let v: Vec<i32> = rows.map(|r| r.unwrap().get(0)).collect();
|
let mut v = Vec::<i32>::new();
|
||||||
|
|
||||||
|
while let Some(row) = rows.next() {
|
||||||
|
v.push(row.unwrap().get(0));
|
||||||
|
}
|
||||||
|
|
||||||
assert_eq!(v, [2i32, 1]);
|
assert_eq!(v, [2i32, 1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1358,26 +1325,6 @@ mod test {
|
|||||||
assert!(format!("{}", err).contains("does_not_exist"));
|
assert!(format!("{}", err).contains("does_not_exist"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_row_expiration() {
|
|
||||||
let db = checked_memory_handle();
|
|
||||||
db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap();
|
|
||||||
db.execute_batch("INSERT INTO foo(x) VALUES(1)").unwrap();
|
|
||||||
db.execute_batch("INSERT INTO foo(x) VALUES(2)").unwrap();
|
|
||||||
|
|
||||||
let mut stmt = db.prepare("SELECT x FROM foo ORDER BY x").unwrap();
|
|
||||||
let mut rows = stmt.query(&[]).unwrap();
|
|
||||||
let first = rows.next().unwrap().unwrap();
|
|
||||||
let second = rows.next().unwrap().unwrap();
|
|
||||||
|
|
||||||
assert_eq!(2i32, second.get(0));
|
|
||||||
|
|
||||||
match first.get_checked::<i32, i32>(0).unwrap_err() {
|
|
||||||
Error::GetFromStaleRow => (),
|
|
||||||
err => panic!("Unexpected error {}", err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_last_insert_rowid() {
|
fn test_last_insert_rowid() {
|
||||||
let db = checked_memory_handle();
|
let db = checked_memory_handle();
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
use std::convert;
|
||||||
|
use std::result;
|
||||||
use libc::c_int;
|
use libc::c_int;
|
||||||
|
|
||||||
use {Result, Error, Connection, Statement, Rows, Row, str_to_cstring};
|
use {Result, Error, Connection, Statement, MappedRows, AndThenRows, Rows, Row, str_to_cstring};
|
||||||
use types::ToSql;
|
use types::ToSql;
|
||||||
|
|
||||||
impl Connection {
|
impl Connection {
|
||||||
@ -84,7 +86,7 @@ impl<'conn> Statement<'conn> {
|
|||||||
self.execute_()
|
self.execute_()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute the prepared statement with named parameter(s), returning an iterator over the
|
/// 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
|
/// 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
|
/// `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.
|
/// `query_named`, or `NULL` if they have never been bound.
|
||||||
@ -96,7 +98,7 @@ impl<'conn> Statement<'conn> {
|
|||||||
/// fn query(conn: &Connection) -> Result<()> {
|
/// fn query(conn: &Connection) -> Result<()> {
|
||||||
/// let mut stmt = try!(conn.prepare("SELECT * FROM test where name = :name"));
|
/// let mut stmt = try!(conn.prepare("SELECT * FROM test where name = :name"));
|
||||||
/// let mut rows = try!(stmt.query_named(&[(":name", &"one")]));
|
/// let mut rows = try!(stmt.query_named(&[(":name", &"one")]));
|
||||||
/// for row in rows {
|
/// while let Some(row) = rows.next() {
|
||||||
/// // ...
|
/// // ...
|
||||||
/// }
|
/// }
|
||||||
/// Ok(())
|
/// Ok(())
|
||||||
@ -111,6 +113,94 @@ impl<'conn> Statement<'conn> {
|
|||||||
Ok(Rows::new(self))
|
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<Vec<String>> {
|
||||||
|
/// 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<MappedRows<'a, F>>
|
||||||
|
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<Person> {
|
||||||
|
/// // ... check for valid name
|
||||||
|
/// Ok(Person{ name: name })
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn get_names(conn: &Connection) -> Result<Vec<Person>> {
|
||||||
|
/// 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<AndThenRows<'a, F>>
|
||||||
|
where E: convert::From<Error>,
|
||||||
|
F: FnMut(&Row) -> result::Result<T, E>
|
||||||
|
{
|
||||||
|
let rows = try!(self.query_named(params));
|
||||||
|
Ok(AndThenRows {
|
||||||
|
rows: rows,
|
||||||
|
map: f,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn bind_parameters_named(&mut self, params: &[(&str, &ToSql)]) -> Result<()> {
|
fn bind_parameters_named(&mut self, params: &[(&str, &ToSql)]) -> Result<()> {
|
||||||
for &(name, value) in params {
|
for &(name, value) in params {
|
||||||
if let Some(i) = try!(self.parameter_index(name)) {
|
if let Some(i) = try!(self.parameter_index(name)) {
|
||||||
@ -126,6 +216,7 @@ impl<'conn> Statement<'conn> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use Connection;
|
use Connection;
|
||||||
|
use error::Error;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_execute_named() {
|
fn test_execute_named() {
|
||||||
@ -139,9 +230,9 @@ mod test {
|
|||||||
|
|
||||||
assert_eq!(3i32,
|
assert_eq!(3i32,
|
||||||
db.query_row_named("SELECT SUM(x) FROM foo WHERE x > :x",
|
db.query_row_named("SELECT SUM(x) FROM foo WHERE x > :x",
|
||||||
&[(":x", &0i32)],
|
&[(":x", &0i32)],
|
||||||
|r| r.get(0))
|
|r| r.get(0))
|
||||||
.unwrap());
|
.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -156,20 +247,77 @@ mod test {
|
|||||||
|
|
||||||
assert_eq!(1i32,
|
assert_eq!(1i32,
|
||||||
db.query_row_named("SELECT COUNT(*) FROM test WHERE name = :name",
|
db.query_row_named("SELECT COUNT(*) FROM test WHERE name = :name",
|
||||||
&[(":name", &"one")],
|
&[(":name", &"one")],
|
||||||
|r| r.get(0))
|
|r| r.get(0))
|
||||||
.unwrap());
|
.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_query_named() {
|
fn test_query_named() {
|
||||||
let db = Connection::open_in_memory().unwrap();
|
let db = Connection::open_in_memory().unwrap();
|
||||||
let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \
|
let sql = r#"
|
||||||
INTEGER)";
|
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();
|
db.execute_batch(sql).unwrap();
|
||||||
|
|
||||||
let mut stmt = db.prepare("SELECT * FROM test where name = :name").unwrap();
|
let mut stmt = db.prepare("SELECT id FROM test where name = :name").unwrap();
|
||||||
stmt.query_named(&[(":name", &"one")]).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]
|
#[test]
|
||||||
|
@ -44,11 +44,11 @@ pub type SqliteTransaction<'conn> = Transaction<'conn>;
|
|||||||
/// # use rusqlite::{Connection, Result};
|
/// # use rusqlite::{Connection, Result};
|
||||||
/// # fn do_queries_part_1(conn: &Connection) -> Result<()> { Ok(()) }
|
/// # fn do_queries_part_1(conn: &Connection) -> Result<()> { Ok(()) }
|
||||||
/// # fn do_queries_part_2(conn: &Connection) -> Result<()> { Ok(()) }
|
/// # fn do_queries_part_2(conn: &Connection) -> Result<()> { Ok(()) }
|
||||||
/// fn perform_queries(conn: &Connection) -> Result<()> {
|
/// fn perform_queries(conn: &mut Connection) -> Result<()> {
|
||||||
/// let tx = try!(conn.transaction());
|
/// let tx = try!(conn.transaction());
|
||||||
///
|
///
|
||||||
/// try!(do_queries_part_1(conn)); // tx causes rollback if this fails
|
/// try!(do_queries_part_1(&tx)); // tx causes rollback if this fails
|
||||||
/// try!(do_queries_part_2(conn)); // tx causes rollback if this fails
|
/// try!(do_queries_part_2(&tx)); // tx causes rollback if this fails
|
||||||
///
|
///
|
||||||
/// tx.commit()
|
/// tx.commit()
|
||||||
/// }
|
/// }
|
||||||
@ -73,11 +73,11 @@ pub struct Transaction<'conn> {
|
|||||||
/// # use rusqlite::{Connection, Result};
|
/// # use rusqlite::{Connection, Result};
|
||||||
/// # fn do_queries_part_1(conn: &Connection) -> Result<()> { Ok(()) }
|
/// # fn do_queries_part_1(conn: &Connection) -> Result<()> { Ok(()) }
|
||||||
/// # fn do_queries_part_2(conn: &Connection) -> Result<()> { Ok(()) }
|
/// # fn do_queries_part_2(conn: &Connection) -> Result<()> { Ok(()) }
|
||||||
/// fn perform_queries(conn: &Connection) -> Result<()> {
|
/// fn perform_queries(conn: &mut Connection) -> Result<()> {
|
||||||
/// let sp = try!(conn.savepoint());
|
/// let sp = try!(conn.savepoint());
|
||||||
///
|
///
|
||||||
/// try!(do_queries_part_1(conn)); // sp causes rollback if this fails
|
/// try!(do_queries_part_1(&sp)); // sp causes rollback if this fails
|
||||||
/// try!(do_queries_part_2(conn)); // sp causes rollback if this fails
|
/// try!(do_queries_part_2(&sp)); // sp causes rollback if this fails
|
||||||
///
|
///
|
||||||
/// sp.commit()
|
/// sp.commit()
|
||||||
/// }
|
/// }
|
||||||
@ -119,12 +119,12 @@ impl<'conn> Transaction<'conn> {
|
|||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// # use rusqlite::{Connection, Result};
|
/// # use rusqlite::{Connection, Result};
|
||||||
/// # fn perform_queries_part_1_succeeds(conn: &Connection) -> bool { true }
|
/// # fn perform_queries_part_1_succeeds(conn: &Connection) -> bool { true }
|
||||||
/// fn perform_queries(conn: &Connection) -> Result<()> {
|
/// fn perform_queries(conn: &mut Connection) -> Result<()> {
|
||||||
/// let tx = try!(conn.transaction());
|
/// let mut tx = try!(conn.transaction());
|
||||||
///
|
///
|
||||||
/// {
|
/// {
|
||||||
/// let sp = try!(tx.savepoint());
|
/// let sp = try!(tx.savepoint());
|
||||||
/// if perform_queries_part_1_succeeds(conn) {
|
/// if perform_queries_part_1_succeeds(&sp) {
|
||||||
/// try!(sp.commit());
|
/// try!(sp.commit());
|
||||||
/// }
|
/// }
|
||||||
/// // otherwise, sp will rollback
|
/// // otherwise, sp will rollback
|
||||||
@ -325,11 +325,11 @@ impl Connection {
|
|||||||
/// # use rusqlite::{Connection, Result};
|
/// # use rusqlite::{Connection, Result};
|
||||||
/// # fn do_queries_part_1(conn: &Connection) -> Result<()> { Ok(()) }
|
/// # fn do_queries_part_1(conn: &Connection) -> Result<()> { Ok(()) }
|
||||||
/// # fn do_queries_part_2(conn: &Connection) -> Result<()> { Ok(()) }
|
/// # fn do_queries_part_2(conn: &Connection) -> Result<()> { Ok(()) }
|
||||||
/// fn perform_queries(conn: &Connection) -> Result<()> {
|
/// fn perform_queries(conn: &mut Connection) -> Result<()> {
|
||||||
/// let tx = try!(conn.transaction());
|
/// let tx = try!(conn.transaction());
|
||||||
///
|
///
|
||||||
/// try!(do_queries_part_1(conn)); // tx causes rollback if this fails
|
/// try!(do_queries_part_1(&tx)); // tx causes rollback if this fails
|
||||||
/// try!(do_queries_part_2(conn)); // tx causes rollback if this fails
|
/// try!(do_queries_part_2(&tx)); // tx causes rollback if this fails
|
||||||
///
|
///
|
||||||
/// tx.commit()
|
/// tx.commit()
|
||||||
/// }
|
/// }
|
||||||
@ -364,11 +364,11 @@ impl Connection {
|
|||||||
/// # use rusqlite::{Connection, Result};
|
/// # use rusqlite::{Connection, Result};
|
||||||
/// # fn do_queries_part_1(conn: &Connection) -> Result<()> { Ok(()) }
|
/// # fn do_queries_part_1(conn: &Connection) -> Result<()> { Ok(()) }
|
||||||
/// # fn do_queries_part_2(conn: &Connection) -> Result<()> { Ok(()) }
|
/// # fn do_queries_part_2(conn: &Connection) -> Result<()> { Ok(()) }
|
||||||
/// fn perform_queries(conn: &Connection) -> Result<()> {
|
/// fn perform_queries(conn: &mut Connection) -> Result<()> {
|
||||||
/// let sp = try!(conn.savepoint());
|
/// let sp = try!(conn.savepoint());
|
||||||
///
|
///
|
||||||
/// try!(do_queries_part_1(conn)); // sp causes rollback if this fails
|
/// try!(do_queries_part_1(&sp)); // sp causes rollback if this fails
|
||||||
/// try!(do_queries_part_2(conn)); // sp causes rollback if this fails
|
/// try!(do_queries_part_2(&sp)); // sp causes rollback if this fails
|
||||||
///
|
///
|
||||||
/// sp.commit()
|
/// sp.commit()
|
||||||
/// }
|
/// }
|
||||||
|
@ -365,17 +365,21 @@ mod test {
|
|||||||
let mut stmt = db.prepare("SELECT t, b FROM foo ORDER BY ROWID ASC").unwrap();
|
let mut stmt = db.prepare("SELECT t, b FROM foo ORDER BY ROWID ASC").unwrap();
|
||||||
let mut rows = stmt.query(&[]).unwrap();
|
let mut rows = stmt.query(&[]).unwrap();
|
||||||
|
|
||||||
let row1 = rows.next().unwrap().unwrap();
|
{
|
||||||
let s1: Option<String> = row1.get(0);
|
let row1 = rows.next().unwrap().unwrap();
|
||||||
let b1: Option<Vec<u8>> = row1.get(1);
|
let s1: Option<String> = row1.get(0);
|
||||||
assert_eq!(s.unwrap(), s1.unwrap());
|
let b1: Option<Vec<u8>> = row1.get(1);
|
||||||
assert!(b1.is_none());
|
assert_eq!(s.unwrap(), s1.unwrap());
|
||||||
|
assert!(b1.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
let row2 = rows.next().unwrap().unwrap();
|
{
|
||||||
let s2: Option<String> = row2.get(0);
|
let row2 = rows.next().unwrap().unwrap();
|
||||||
let b2: Option<Vec<u8>> = row2.get(1);
|
let s2: Option<String> = row2.get(0);
|
||||||
assert!(s2.is_none());
|
let b2: Option<Vec<u8>> = row2.get(1);
|
||||||
assert_eq!(b, b2);
|
assert!(s2.is_none());
|
||||||
|
assert_eq!(b, b2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
Loading…
Reference in New Issue
Block a user