mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-26 19:41:37 +08:00
Merge pull request #126 from jgallagher/make-readme-less-scary
Remove scary lifetime-of-rows-may-panic from README.
This commit is contained in:
commit
4c97a0b069
39
README.md
39
README.md
@ -73,45 +73,6 @@ features](http://doc.crates.io/manifest.html#the-features-section). They are:
|
|||||||
* [`blob`](http://jgallagher.github.io/rusqlite/rusqlite/blob/index.html)
|
* [`blob`](http://jgallagher.github.io/rusqlite/rusqlite/blob/index.html)
|
||||||
gives `std::io::{Read, Write, Seek}` access to SQL BLOBs.
|
gives `std::io::{Read, Write, Seek}` access to SQL BLOBs.
|
||||||
|
|
||||||
### Design of Rows and Row
|
|
||||||
|
|
||||||
To retrieve the result rows from a query, SQLite requires you to call
|
|
||||||
[sqlite3_step()](https://www.sqlite.org/c3ref/step.html) on a prepared statement. You can only
|
|
||||||
retrieve the values of the "current" row. From the Rust point of view, this means that each row
|
|
||||||
is only valid until the next row is fetched. [rust-sqlite3](https://github.com/dckc/rust-sqlite3)
|
|
||||||
solves this the correct way with lifetimes. However, this means that the result rows do not
|
|
||||||
satisfy the [Iterator](http://doc.rust-lang.org/std/iter/trait.Iterator.html) trait, which means
|
|
||||||
you cannot (as easily) loop over the rows, or use many of the helpful Iterator methods like `map`
|
|
||||||
and `filter`.
|
|
||||||
|
|
||||||
Instead, Rusqlite's `Rows` handle does conform to `Iterator`. It ensures safety by
|
|
||||||
performing checks at runtime to ensure you do not try to retrieve the values of a "stale" row, and
|
|
||||||
will panic if you do so. A specific example that will panic:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
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 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)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
There are other, less obvious things that may result in a panic as well, such as calling
|
|
||||||
`collect()` on a `Rows` and then trying to use the collected rows.
|
|
||||||
|
|
||||||
Strongly consider using the method `query_map()` instead, if you can.
|
|
||||||
`query_map()` returns an iterator over rows-mapped-to-some-type. This
|
|
||||||
iterator does not have any of the above issues with panics due to attempting to
|
|
||||||
access stale rows.
|
|
||||||
|
|
||||||
## Author
|
## Author
|
||||||
|
|
||||||
John Gallagher, johnkgallagher@gmail.com
|
John Gallagher, johnkgallagher@gmail.com
|
||||||
|
11
src/lib.rs
11
src/lib.rs
@ -983,6 +983,9 @@ pub type SqliteRows<'stmt> = Rows<'stmt>;
|
|||||||
///
|
///
|
||||||
/// ## Warning
|
/// ## 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
|
/// 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`
|
/// from a row after it has become stale (i.e., `next()` has been called again on the `Rows`
|
||||||
/// iterator). For example:
|
/// iterator). For example:
|
||||||
@ -994,7 +997,7 @@ pub type SqliteRows<'stmt> = Rows<'stmt>;
|
|||||||
/// let mut rows = try!(stmt.query(&[]));
|
/// let mut rows = try!(stmt.query(&[]));
|
||||||
///
|
///
|
||||||
/// let row0 = try!(rows.next().unwrap());
|
/// let row0 = try!(rows.next().unwrap());
|
||||||
/// // row 0 is value now...
|
/// // row 0 is valid for now...
|
||||||
///
|
///
|
||||||
/// let row1 = try!(rows.next().unwrap());
|
/// let row1 = try!(rows.next().unwrap());
|
||||||
/// // row 0 is now STALE, and row 1 is valid
|
/// // row 0 is now STALE, and row 1 is valid
|
||||||
@ -1008,12 +1011,6 @@ pub type SqliteRows<'stmt> = Rows<'stmt>;
|
|||||||
/// (which would result in a collection of rows, only the last of which can safely be used) and
|
/// (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,
|
/// `min`/`max` (which could return a stale row unless the last row happened to be the min or max,
|
||||||
/// respectively).
|
/// respectively).
|
||||||
///
|
|
||||||
/// This problem could be solved by changing the signature of `next` to tie the lifetime of the
|
|
||||||
/// returned row to the lifetime of (a mutable reference to) the result rows handle, but this would
|
|
||||||
/// no longer implement `Iterator`, and therefore you would lose access to the majority of
|
|
||||||
/// functions which are useful (such as support for `for ... in ...` looping, `map`, `filter`,
|
|
||||||
/// etc.).
|
|
||||||
pub struct Rows<'stmt> {
|
pub struct Rows<'stmt> {
|
||||||
stmt: &'stmt Statement<'stmt>,
|
stmt: &'stmt Statement<'stmt>,
|
||||||
current_row: Rc<Cell<c_int>>,
|
current_row: Rc<Cell<c_int>>,
|
||||||
|
Loading…
Reference in New Issue
Block a user