mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-26 11:31:37 +08:00
Add documentation
This commit is contained in:
parent
949260046c
commit
b34f255aac
258
src/lib.rs
258
src/lib.rs
@ -1,3 +1,52 @@
|
|||||||
|
//! Rusqlite is an ergonomic, semi-safe wrapper for using SQLite from Rust. It attempts to expose
|
||||||
|
//! an interface similar to [rust-postgres](https://github.com/sfackler/rust-postgres).
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! extern crate rusqlite;
|
||||||
|
//! extern crate time;
|
||||||
|
//!
|
||||||
|
//! use time::Timespec;
|
||||||
|
//! use rusqlite::SqliteConnection;
|
||||||
|
//!
|
||||||
|
//! #[deriving(Show)]
|
||||||
|
//! struct Person {
|
||||||
|
//! id: i32,
|
||||||
|
//! name: String,
|
||||||
|
//! time_created: Timespec,
|
||||||
|
//! data: Option<Vec<u8>>
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! fn main() {
|
||||||
|
//! let conn = SqliteConnection::open(":memory:").unwrap();
|
||||||
|
//!
|
||||||
|
//! conn.execute("CREATE TABLE person (
|
||||||
|
//! id INTEGER PRIMARY KEY,
|
||||||
|
//! name TEXT NOT NULL,
|
||||||
|
//! time_created TEXT NOT NULL,
|
||||||
|
//! data BLOB
|
||||||
|
//! )", []).unwrap();
|
||||||
|
//! let me = Person {
|
||||||
|
//! id: 0,
|
||||||
|
//! name: "Steven".to_string(),
|
||||||
|
//! time_created: time::get_time(),
|
||||||
|
//! data: None
|
||||||
|
//! };
|
||||||
|
//! conn.execute("INSERT INTO person (name, time_created, data)
|
||||||
|
//! VALUES ($1, $2, $3)",
|
||||||
|
//! &[&me.name, &me.time_created, &me.data]).unwrap();
|
||||||
|
//!
|
||||||
|
//! let mut stmt = conn.prepare("SELECT id, name, time_created, data FROM person").unwrap();
|
||||||
|
//! for row in stmt.query([]).unwrap().map(|row| row.unwrap()) {
|
||||||
|
//! let person = Person {
|
||||||
|
//! id: row.get(0),
|
||||||
|
//! name: row.get(1),
|
||||||
|
//! time_created: row.get(2),
|
||||||
|
//! data: row.get(3)
|
||||||
|
//! };
|
||||||
|
//! println!("Found person {}", person);
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
#![feature(globs)]
|
#![feature(globs)]
|
||||||
#![feature(unsafe_destructor)]
|
#![feature(unsafe_destructor)]
|
||||||
#![feature(macro_rules)]
|
#![feature(macro_rules)]
|
||||||
@ -22,8 +71,11 @@ pub use transaction::{SqliteTransactionBehavior,
|
|||||||
|
|
||||||
pub mod types;
|
pub mod types;
|
||||||
mod transaction;
|
mod transaction;
|
||||||
|
|
||||||
|
/// Automatically generated FFI bindings (via [bindgen](https://github.com/crabtw/rust-bindgen)).
|
||||||
#[allow(dead_code,non_snake_case,non_camel_case_types)] pub mod ffi;
|
#[allow(dead_code,non_snake_case,non_camel_case_types)] pub mod ffi;
|
||||||
|
|
||||||
|
/// A typedef of the result returned by many methods.
|
||||||
pub type SqliteResult<T> = Result<T, SqliteError>;
|
pub type SqliteResult<T> = Result<T, SqliteError>;
|
||||||
|
|
||||||
unsafe fn errmsg_to_string(errmsg: *const c_char) -> String {
|
unsafe fn errmsg_to_string(errmsg: *const c_char) -> String {
|
||||||
@ -31,9 +83,15 @@ unsafe fn errmsg_to_string(errmsg: *const c_char) -> String {
|
|||||||
c_str.as_str().unwrap_or("Invalid error message encoding").to_string()
|
c_str.as_str().unwrap_or("Invalid error message encoding").to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Encompasses an error result from a call to the SQLite C API.
|
||||||
#[deriving(Show)]
|
#[deriving(Show)]
|
||||||
pub struct SqliteError {
|
pub struct SqliteError {
|
||||||
|
/// The error code returned by a SQLite C API call. See [SQLite Result
|
||||||
|
/// Codes](http://www.sqlite.org/rescode.html) for details.
|
||||||
pub code: c_int,
|
pub code: c_int,
|
||||||
|
|
||||||
|
/// The error message provided by [sqlite3_errmsg](http://www.sqlite.org/c3ref/errcode.html),
|
||||||
|
/// if possible, or a generic error message based on `code` otherwise.
|
||||||
pub message: String,
|
pub message: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,53 +106,170 @@ impl SqliteError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A connection to a SQLite database.
|
||||||
|
///
|
||||||
|
/// ## Warning
|
||||||
|
///
|
||||||
|
/// Note that despite the fact that most `SqliteConnection` methods take an immutable reference to
|
||||||
|
/// `self`, `SqliteConnection` is NOT threadsafe, and using it from multiple threads may result in
|
||||||
|
/// runtime panics or data races. The SQLite connection handle has at least two pieces of internal
|
||||||
|
/// state (the last insertion ID and the last error message) that rusqlite uses, but wrapping these
|
||||||
|
/// APIs in a safe way from Rust would be too restrictive (for example, you would not be able to
|
||||||
|
/// prepare multiple statements at the same time).
|
||||||
pub struct SqliteConnection {
|
pub struct SqliteConnection {
|
||||||
db: RefCell<InnerSqliteConnection>,
|
db: RefCell<InnerSqliteConnection>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SqliteConnection {
|
impl SqliteConnection {
|
||||||
|
/// Open a new connection to a SQLite database.
|
||||||
|
///
|
||||||
|
/// Use the special path `:memory:` to create an in-memory database.
|
||||||
|
/// `SqliteConnection::open(path)` is equivalent to `SqliteConnection::open_with_flags(path,
|
||||||
|
/// SQLITE_OPEN_READ_WRITE | SQLITE_OPEN_CREATE)`.
|
||||||
pub fn open(path: &str) -> SqliteResult<SqliteConnection> {
|
pub fn open(path: &str) -> SqliteResult<SqliteConnection> {
|
||||||
let flags = SQLITE_OPEN_READ_WRITE | SQLITE_OPEN_CREATE;
|
let flags = SQLITE_OPEN_READ_WRITE | SQLITE_OPEN_CREATE;
|
||||||
SqliteConnection::open_with_flags(path, flags)
|
SqliteConnection::open_with_flags(path, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Open a new connection to a SQLite database.
|
||||||
|
///
|
||||||
|
/// Use the special path `:memory:` to create an in-memory database. See [Opening A New
|
||||||
|
/// Database Connection](http://www.sqlite.org/c3ref/open.html) for a description of valid
|
||||||
|
/// flag combinations.
|
||||||
pub fn open_with_flags(path: &str, flags: SqliteOpenFlags) -> SqliteResult<SqliteConnection> {
|
pub fn open_with_flags(path: &str, flags: SqliteOpenFlags) -> SqliteResult<SqliteConnection> {
|
||||||
InnerSqliteConnection::open_with_flags(path, flags).map(|db| {
|
InnerSqliteConnection::open_with_flags(path, flags).map(|db| {
|
||||||
SqliteConnection{ db: RefCell::new(db) }
|
SqliteConnection{ db: RefCell::new(db) }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Begin a new transaction with the default behavior (DEFERRED).
|
||||||
|
///
|
||||||
|
/// The transaction defaults to rolling back when it is dropped. If you want the transaction to
|
||||||
|
/// commit, you must call `commit` or `set_commit`.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// # use rusqlite::{SqliteConnection, SqliteResult};
|
||||||
|
/// # fn do_queries_part_1(conn: &SqliteConnection) -> SqliteResult<()> { Ok(()) }
|
||||||
|
/// # fn do_queries_part_2(conn: &SqliteConnection) -> SqliteResult<()> { Ok(()) }
|
||||||
|
/// fn perform_queries(conn: &SqliteConnection) -> SqliteResult<()> {
|
||||||
|
/// let tx = try!(conn.transaction());
|
||||||
|
///
|
||||||
|
/// try!(do_queries_part_1(conn)); // tx causes rollback if this fails
|
||||||
|
/// try!(do_queries_part_2(conn)); // tx causes rollback if this fails
|
||||||
|
///
|
||||||
|
/// tx.commit()
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
pub fn transaction<'a>(&'a self) -> SqliteResult<SqliteTransaction<'a>> {
|
pub fn transaction<'a>(&'a self) -> SqliteResult<SqliteTransaction<'a>> {
|
||||||
SqliteTransaction::new(self, SqliteTransactionDeferred)
|
SqliteTransaction::new(self, SqliteTransactionDeferred)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Begin a new transaction with a specified behavior.
|
||||||
|
///
|
||||||
|
/// See `transaction`.
|
||||||
pub fn transaction_with_behavior<'a>(&'a self, behavior: SqliteTransactionBehavior)
|
pub fn transaction_with_behavior<'a>(&'a self, behavior: SqliteTransactionBehavior)
|
||||||
-> SqliteResult<SqliteTransaction<'a>> {
|
-> SqliteResult<SqliteTransaction<'a>> {
|
||||||
SqliteTransaction::new(self, behavior)
|
SqliteTransaction::new(self, behavior)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convenience method to run multiple SQL statements (that cannot take any parameters).
|
||||||
|
///
|
||||||
|
/// Uses [sqlite3_exec](http://www.sqlite.org/c3ref/exec.html) under the hood.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// # use rusqlite::{SqliteConnection, SqliteResult};
|
||||||
|
/// fn create_tables(conn: &SqliteConnection) -> SqliteResult<()> {
|
||||||
|
/// conn.execute_batch("BEGIN;
|
||||||
|
/// CREATE TABLE foo(x INTEGER);
|
||||||
|
/// CREATE TABLE bar(y TEXT);
|
||||||
|
/// COMMIT;")
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
pub fn execute_batch(&self, sql: &str) -> SqliteResult<()> {
|
pub fn execute_batch(&self, sql: &str) -> SqliteResult<()> {
|
||||||
self.db.borrow_mut().execute_batch(sql)
|
self.db.borrow_mut().execute_batch(sql)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convenience method to prepare and execute a single SQL statement.
|
||||||
|
///
|
||||||
|
/// On success, returns the number of rows that were changed or inserted or deleted (via
|
||||||
|
/// `sqlite3_changes`).
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// # use rusqlite::{SqliteConnection};
|
||||||
|
/// fn update_rows(conn: &SqliteConnection) {
|
||||||
|
/// match conn.execute("UPDATE foo SET bar = 'baz' WHERE qux = ?", &[&1i32]) {
|
||||||
|
/// Ok(updated) => println!("{} rows were updated", updated),
|
||||||
|
/// Err(err) => println!("update failed: {}", err),
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
pub fn execute(&self, sql: &str, params: &[&ToSql]) -> SqliteResult<uint> {
|
pub fn execute(&self, sql: &str, params: &[&ToSql]) -> SqliteResult<uint> {
|
||||||
self.prepare(sql).and_then(|mut stmt| stmt.execute(params))
|
self.prepare(sql).and_then(|mut stmt| stmt.execute(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
|
||||||
|
/// the hood.
|
||||||
pub fn last_insert_rowid(&self) -> i64 {
|
pub fn last_insert_rowid(&self) -> i64 {
|
||||||
self.db.borrow_mut().last_insert_rowid()
|
self.db.borrow_mut().last_insert_rowid()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convenience method to execute a query that is expected to return a single row.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// # use rusqlite::{SqliteConnection};
|
||||||
|
/// fn preferred_locale(conn: &SqliteConnection) -> String {
|
||||||
|
/// conn.query_row("SELECT value FROM preferences WHERE name='locale'", [], |row| {
|
||||||
|
/// row.get(0)
|
||||||
|
/// })
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Failure
|
||||||
|
///
|
||||||
|
/// Panics if:
|
||||||
|
///
|
||||||
|
/// * Preparing the query fails.
|
||||||
|
/// * Running the query fails (i.e., calling `query` on the prepared statement).
|
||||||
|
/// * The query does not successfully return at least one row.
|
||||||
|
///
|
||||||
|
/// If the query returns more than one row, all rows except the first are ignored.
|
||||||
pub fn query_row<T>(&self, sql: &str, params: &[&ToSql], f: |SqliteRow| -> T) -> T {
|
pub fn query_row<T>(&self, sql: &str, params: &[&ToSql], f: |SqliteRow| -> T) -> T {
|
||||||
let mut stmt = self.prepare(sql).unwrap();
|
let mut stmt = self.prepare(sql).unwrap();
|
||||||
let mut rows = stmt.query(params).unwrap();
|
let mut rows = stmt.query(params).unwrap();
|
||||||
f(rows.next().expect("Query did not return a row").unwrap())
|
f(rows.next().expect("Query did not return a row").unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Prepare a SQL statement for execution.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// # use rusqlite::{SqliteConnection, SqliteResult};
|
||||||
|
/// fn insert_new_people(conn: &SqliteConnection) -> SqliteResult<()> {
|
||||||
|
/// let mut stmt = try!(conn.prepare("INSERT INTO People (name) VALUES (?)"));
|
||||||
|
/// try!(stmt.execute(&[&"Joe Smith"]));
|
||||||
|
/// try!(stmt.execute(&[&"Bob Jones"]));
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
pub fn prepare<'a>(&'a self, sql: &str) -> SqliteResult<SqliteStatement<'a>> {
|
pub fn prepare<'a>(&'a self, sql: &str) -> SqliteResult<SqliteStatement<'a>> {
|
||||||
self.db.borrow_mut().prepare(self, sql)
|
self.db.borrow_mut().prepare(self, sql)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Close the SQLite connection.
|
||||||
|
///
|
||||||
|
/// This is functionally equivalent to the `Drop` implementation for `SqliteConnection` except
|
||||||
|
/// that it returns any error encountered to the caller.
|
||||||
pub fn close(self) -> SqliteResult<()> {
|
pub fn close(self) -> SqliteResult<()> {
|
||||||
self.db.borrow_mut().close()
|
self.db.borrow_mut().close()
|
||||||
}
|
}
|
||||||
@ -119,7 +294,10 @@ struct InnerSqliteConnection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
#[repr(C)] flags SqliteOpenFlags: c_int {
|
#[doc = "Flags for opening SQLite database connections."]
|
||||||
|
#[doc = "See [sqlite3_open_v2](http://www.sqlite.org/c3ref/open.html) for details."]
|
||||||
|
#[repr(C)]
|
||||||
|
flags SqliteOpenFlags: c_int {
|
||||||
const SQLITE_OPEN_READ_ONLY = 0x00000001,
|
const SQLITE_OPEN_READ_ONLY = 0x00000001,
|
||||||
const SQLITE_OPEN_READ_WRITE = 0x00000002,
|
const SQLITE_OPEN_READ_WRITE = 0x00000002,
|
||||||
const SQLITE_OPEN_CREATE = 0x00000004,
|
const SQLITE_OPEN_CREATE = 0x00000004,
|
||||||
@ -211,6 +389,7 @@ impl Drop for InnerSqliteConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A prepared statement.
|
||||||
pub struct SqliteStatement<'conn> {
|
pub struct SqliteStatement<'conn> {
|
||||||
conn: &'conn SqliteConnection,
|
conn: &'conn SqliteConnection,
|
||||||
stmt: *mut ffi::sqlite3_stmt,
|
stmt: *mut ffi::sqlite3_stmt,
|
||||||
@ -222,6 +401,24 @@ impl<'conn> SqliteStatement<'conn> {
|
|||||||
SqliteStatement{ conn: conn, stmt: stmt, needs_reset: false }
|
SqliteStatement{ conn: conn, stmt: stmt, needs_reset: false }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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::{SqliteConnection, SqliteResult};
|
||||||
|
/// fn update_rows(conn: &SqliteConnection) -> SqliteResult<()> {
|
||||||
|
/// let mut stmt = try!(conn.prepare("UPDATE foo SET bar = 'baz' WHERE qux = ?"));
|
||||||
|
///
|
||||||
|
/// try!(stmt.execute(&[&1i32]));
|
||||||
|
/// try!(stmt.execute(&[&2i32]));
|
||||||
|
///
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
pub fn execute(&mut self, params: &[&ToSql]) -> SqliteResult<uint> {
|
pub fn execute(&mut self, params: &[&ToSql]) -> SqliteResult<uint> {
|
||||||
self.reset_if_needed();
|
self.reset_if_needed();
|
||||||
|
|
||||||
@ -243,6 +440,25 @@ impl<'conn> SqliteStatement<'conn> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Execute the prepared statement, returning an iterator over the resulting rows.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// # use rusqlite::{SqliteConnection, SqliteResult};
|
||||||
|
/// fn get_names(conn: &SqliteConnection) -> SqliteResult<Vec<String>> {
|
||||||
|
/// let mut stmt = try!(conn.prepare("SELECT name FROM people"));
|
||||||
|
/// let mut rows = try!(stmt.query([]));
|
||||||
|
///
|
||||||
|
/// let mut names = Vec::new();
|
||||||
|
/// for result_row in rows {
|
||||||
|
/// let row = try!(result_row);
|
||||||
|
/// names.push(row.get(0));
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// Ok(names)
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
pub fn query<'a>(&'a mut self, params: &[&ToSql]) -> SqliteResult<SqliteRows<'a>> {
|
pub fn query<'a>(&'a mut self, params: &[&ToSql]) -> SqliteResult<SqliteRows<'a>> {
|
||||||
self.reset_if_needed();
|
self.reset_if_needed();
|
||||||
|
|
||||||
@ -258,6 +474,10 @@ impl<'conn> SqliteStatement<'conn> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Consumes the statement.
|
||||||
|
///
|
||||||
|
/// Functionally equivalent to the `Drop` implementation, but allows callers to see any errors
|
||||||
|
/// that occur.
|
||||||
pub fn finalize(mut self) -> SqliteResult<()> {
|
pub fn finalize(mut self) -> SqliteResult<()> {
|
||||||
self.finalize_()
|
self.finalize_()
|
||||||
}
|
}
|
||||||
@ -290,6 +510,7 @@ impl<'conn> Drop for SqliteStatement<'conn> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An iterator over the resulting rows of a query.
|
||||||
pub struct SqliteRows<'stmt> {
|
pub struct SqliteRows<'stmt> {
|
||||||
stmt: &'stmt SqliteStatement<'stmt>,
|
stmt: &'stmt SqliteStatement<'stmt>,
|
||||||
current_row: Rc<Cell<c_int>>,
|
current_row: Rc<Cell<c_int>>,
|
||||||
@ -326,6 +547,7 @@ impl<'stmt> Iterator<SqliteResult<SqliteRow<'stmt>>> for SqliteRows<'stmt> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A single result row of a query.
|
||||||
pub struct SqliteRow<'stmt> {
|
pub struct SqliteRow<'stmt> {
|
||||||
stmt: &'stmt SqliteStatement<'stmt>,
|
stmt: &'stmt SqliteStatement<'stmt>,
|
||||||
current_row: Rc<Cell<c_int>>,
|
current_row: Rc<Cell<c_int>>,
|
||||||
@ -333,10 +555,44 @@ pub struct SqliteRow<'stmt> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'stmt> SqliteRow<'stmt> {
|
impl<'stmt> SqliteRow<'stmt> {
|
||||||
|
/// Get the value of a particular column of the result row.
|
||||||
|
///
|
||||||
|
/// Note that `SqliteRow` falls into the "semi-safe" category of rusqlite. 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 a loop over the rows, for
|
||||||
|
/// example) this isn't an issue, but it means you cannot do something like this:
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// # use rusqlite::{SqliteConnection, SqliteResult};
|
||||||
|
/// fn bad_function_will_panic(conn: &SqliteConnection) -> SqliteResult<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
|
||||||
|
///
|
||||||
|
/// Panics if `idx` is outside the range of columns in the returned query or if this row
|
||||||
|
/// is stale.
|
||||||
pub fn get<T: FromSql>(&self, idx: c_int) -> T {
|
pub fn get<T: FromSql>(&self, idx: c_int) -> T {
|
||||||
self.get_opt(idx).unwrap()
|
self.get_opt(idx).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempt to get the value of a particular column of the result row.
|
||||||
|
///
|
||||||
|
/// ## Failure
|
||||||
|
///
|
||||||
|
/// Returns a `SQLITE_MISUSE`-coded `SqliteError` if `idx` is outside the valid column range
|
||||||
|
/// for this row or if this row is stale.
|
||||||
pub fn get_opt<T: FromSql>(&self, idx: c_int) -> SqliteResult<T> {
|
pub fn get_opt<T: FromSql>(&self, idx: c_int) -> SqliteResult<T> {
|
||||||
if self.row_idx != self.current_row.get() {
|
if self.row_idx != self.current_row.get() {
|
||||||
return Err(SqliteError{ code: ffi::SQLITE_MISUSE,
|
return Err(SqliteError{ code: ffi::SQLITE_MISUSE,
|
||||||
|
@ -1,11 +1,35 @@
|
|||||||
use {SqliteResult, SqliteConnection};
|
use {SqliteResult, SqliteConnection};
|
||||||
|
|
||||||
|
/// Options for transaction behavior. See [BEGIN
|
||||||
|
/// TRANSACTION](http://www.sqlite.org/lang_transaction.html) for details.
|
||||||
pub enum SqliteTransactionBehavior {
|
pub enum SqliteTransactionBehavior {
|
||||||
SqliteTransactionDeferred,
|
SqliteTransactionDeferred,
|
||||||
SqliteTransactionImmediate,
|
SqliteTransactionImmediate,
|
||||||
SqliteTransactionExclusive,
|
SqliteTransactionExclusive,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents a transaction on a database connection.
|
||||||
|
///
|
||||||
|
/// ## Note
|
||||||
|
///
|
||||||
|
/// Transactions will roll back by default. Use the `set_commit` or `commit` methods to commit the
|
||||||
|
/// transaction.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// # use rusqlite::{SqliteConnection, SqliteResult};
|
||||||
|
/// # fn do_queries_part_1(conn: &SqliteConnection) -> SqliteResult<()> { Ok(()) }
|
||||||
|
/// # fn do_queries_part_2(conn: &SqliteConnection) -> SqliteResult<()> { Ok(()) }
|
||||||
|
/// fn perform_queries(conn: &SqliteConnection) -> SqliteResult<()> {
|
||||||
|
/// let tx = try!(conn.transaction());
|
||||||
|
///
|
||||||
|
/// try!(do_queries_part_1(conn)); // tx causes rollback if this fails
|
||||||
|
/// try!(do_queries_part_2(conn)); // tx causes rollback if this fails
|
||||||
|
///
|
||||||
|
/// tx.commit()
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
pub struct SqliteTransaction<'conn> {
|
pub struct SqliteTransaction<'conn> {
|
||||||
conn: &'conn SqliteConnection,
|
conn: &'conn SqliteConnection,
|
||||||
depth: u32,
|
depth: u32,
|
||||||
@ -14,6 +38,7 @@ pub struct SqliteTransaction<'conn> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'conn> SqliteTransaction<'conn> {
|
impl<'conn> SqliteTransaction<'conn> {
|
||||||
|
/// Begin a new transaction. Cannot be nested; see `savepoint` for nested transactions.
|
||||||
pub fn new(conn: &SqliteConnection,
|
pub fn new(conn: &SqliteConnection,
|
||||||
behavior: SqliteTransactionBehavior) -> SqliteResult<SqliteTransaction> {
|
behavior: SqliteTransactionBehavior) -> SqliteResult<SqliteTransaction> {
|
||||||
let query = match behavior {
|
let query = match behavior {
|
||||||
@ -26,6 +51,32 @@ impl<'conn> SqliteTransaction<'conn> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Starts a new [savepoint](http://www.sqlite.org/lang_savepoint.html), allowing nested
|
||||||
|
/// transactions.
|
||||||
|
///
|
||||||
|
/// ## Note
|
||||||
|
///
|
||||||
|
/// Just like outer level transactions, savepoint transactions rollback by default.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// # use rusqlite::{SqliteConnection, SqliteResult};
|
||||||
|
/// # fn perform_queries_part_1_succeeds(conn: &SqliteConnection) -> bool { true }
|
||||||
|
/// fn perform_queries(conn: &SqliteConnection) -> SqliteResult<()> {
|
||||||
|
/// let tx = try!(conn.transaction());
|
||||||
|
///
|
||||||
|
/// {
|
||||||
|
/// let sp = try!(tx.savepoint());
|
||||||
|
/// if perform_queries_part_1_succeeds(conn) {
|
||||||
|
/// try!(sp.commit());
|
||||||
|
/// }
|
||||||
|
/// // otherwise, sp will rollback
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// tx.commit()
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
pub fn savepoint<'a>(&'a self) -> SqliteResult<SqliteTransaction<'a>> {
|
pub fn savepoint<'a>(&'a self) -> SqliteResult<SqliteTransaction<'a>> {
|
||||||
self.conn.execute_batch("SAVEPOINT sp").map(|_| {
|
self.conn.execute_batch("SAVEPOINT sp").map(|_| {
|
||||||
SqliteTransaction{
|
SqliteTransaction{
|
||||||
@ -34,22 +85,27 @@ impl<'conn> SqliteTransaction<'conn> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns whether or not the transaction is currently set to commit.
|
||||||
pub fn will_commit(&self) -> bool {
|
pub fn will_commit(&self) -> bool {
|
||||||
self.commit
|
self.commit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns whether or not the transaction is currently set to rollback.
|
||||||
pub fn will_rollback(&self) -> bool {
|
pub fn will_rollback(&self) -> bool {
|
||||||
!self.commit
|
!self.commit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the transaction to commit at its completion.
|
||||||
pub fn set_commit(&mut self) {
|
pub fn set_commit(&mut self) {
|
||||||
self.commit = true
|
self.commit = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the transaction to rollback at its completion.
|
||||||
pub fn set_rollback(&mut self) {
|
pub fn set_rollback(&mut self) {
|
||||||
self.commit = false
|
self.commit = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A convenience method which consumes and commits a transaction.
|
||||||
pub fn commit(mut self) -> SqliteResult<()> {
|
pub fn commit(mut self) -> SqliteResult<()> {
|
||||||
self.commit_()
|
self.commit_()
|
||||||
}
|
}
|
||||||
@ -59,6 +115,7 @@ impl<'conn> SqliteTransaction<'conn> {
|
|||||||
self.conn.execute_batch(if self.depth == 0 { "COMMIT" } else { "RELEASE sp" })
|
self.conn.execute_batch(if self.depth == 0 { "COMMIT" } else { "RELEASE sp" })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A convenience method which consumes and rolls back a transaction.
|
||||||
pub fn rollback(mut self) -> SqliteResult<()> {
|
pub fn rollback(mut self) -> SqliteResult<()> {
|
||||||
self.rollback_()
|
self.rollback_()
|
||||||
}
|
}
|
||||||
@ -68,6 +125,11 @@ impl<'conn> SqliteTransaction<'conn> {
|
|||||||
self.conn.execute_batch(if self.depth == 0 { "ROLLBACK" } else { "ROLLBACK TO sp" })
|
self.conn.execute_batch(if self.depth == 0 { "ROLLBACK" } else { "ROLLBACK TO sp" })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Consumes the transaction, committing or rolling back according to the current setting
|
||||||
|
/// (see `will_commit`, `will_rollback`).
|
||||||
|
///
|
||||||
|
/// Functionally equivalent to the `Drop` implementation, but allows callers to see any
|
||||||
|
/// errors that occur.
|
||||||
pub fn finish(mut self) -> SqliteResult<()> {
|
pub fn finish(mut self) -> SqliteResult<()> {
|
||||||
self.finish_()
|
self.finish_()
|
||||||
}
|
}
|
||||||
|
67
src/types.rs
67
src/types.rs
@ -1,3 +1,57 @@
|
|||||||
|
//! Traits dealing with SQLite data types.
|
||||||
|
//!
|
||||||
|
//! SQLite uses a [dynamic type system](https://www.sqlite.org/datatype3.html). Implementations of
|
||||||
|
//! the `ToSql` and `FromSql` traits are provided for the basic types that SQLite provides methods
|
||||||
|
//! for:
|
||||||
|
//!
|
||||||
|
//! * C integers and doubles (`c_int` and `c_double`)
|
||||||
|
//! * Strings (`String` and `&str`)
|
||||||
|
//! * Blobs (`Vec<u8>` and `&[u8]`)
|
||||||
|
//!
|
||||||
|
//! Additionally, because it is such a common data type, implementations are provided for
|
||||||
|
//! `time::Timespec` that use a string for storage (using the same format string,
|
||||||
|
//! `"%Y-%m-%d %H:%M:%S"`, as SQLite's builtin
|
||||||
|
//! [datetime](https://www.sqlite.org/lang_datefunc.html) function. Note that this storage
|
||||||
|
//! truncates timespecs to the nearest second. If you want different storage for timespecs, you can
|
||||||
|
//! use a newtype. For example, to store timespecs as doubles:
|
||||||
|
//!
|
||||||
|
//! `ToSql` and `FromSql` are also implemented for `Option<T>` where `T` implements `ToSql` or
|
||||||
|
//! `FromSql` for the cases where you want to know if a value was NULL (which gets translated to
|
||||||
|
//! `None`). If you get a value that was NULL in SQLite but you store it into a non-`Option` value
|
||||||
|
//! in Rust, you will get a "sensible" zero value - 0 for numeric types (including timespecs), an
|
||||||
|
//! empty string, or an empty vector of bytes.
|
||||||
|
//!
|
||||||
|
//! ```rust,ignore
|
||||||
|
//! extern crate rusqlite;
|
||||||
|
//! extern crate libc;
|
||||||
|
//!
|
||||||
|
//! use rusqlite::types::{FromSql, ToSql};
|
||||||
|
//! use rusqlite::{ffi, SqliteResult};
|
||||||
|
//! use libc::c_int;
|
||||||
|
//! use time;
|
||||||
|
//!
|
||||||
|
//! pub struct TimespecSql(pub time::Timespec);
|
||||||
|
//!
|
||||||
|
//! impl FromSql for TimespecSql {
|
||||||
|
//! unsafe fn column_result(stmt: *mut ffi::sqlite3_stmt, col: c_int)
|
||||||
|
//! -> SqliteResult<TimespecSql> {
|
||||||
|
//! let as_f64_result = FromSql::column_result(stmt, col);
|
||||||
|
//! as_f64_result.map(|as_f64: f64| {
|
||||||
|
//! TimespecSql(time::Timespec{ sec: as_f64.trunc() as i64,
|
||||||
|
//! nsec: (as_f64.fract() * 1.0e9) as i32 })
|
||||||
|
//! })
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! impl ToSql for TimespecSql {
|
||||||
|
//! unsafe fn bind_parameter(&self, stmt: *mut ffi::sqlite3_stmt, col: c_int) -> c_int {
|
||||||
|
//! let TimespecSql(ts) = *self;
|
||||||
|
//! let as_f64 = ts.sec as f64 + (ts.nsec as f64) / 1.0e9;
|
||||||
|
//! as_f64.bind_parameter(stmt, col)
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
extern crate time;
|
extern crate time;
|
||||||
|
|
||||||
use libc::{c_int, c_double};
|
use libc::{c_int, c_double};
|
||||||
@ -9,10 +63,12 @@ use super::{SqliteResult, SqliteError};
|
|||||||
|
|
||||||
const SQLITE_DATETIME_FMT: &'static str = "%Y-%m-%d %H:%M:%S";
|
const SQLITE_DATETIME_FMT: &'static str = "%Y-%m-%d %H:%M:%S";
|
||||||
|
|
||||||
|
/// A trait for types that can be converted into SQLite values.
|
||||||
pub trait ToSql {
|
pub trait ToSql {
|
||||||
unsafe fn bind_parameter(&self, stmt: *mut ffi::sqlite3_stmt, col: c_int) -> c_int;
|
unsafe fn bind_parameter(&self, stmt: *mut ffi::sqlite3_stmt, col: c_int) -> c_int;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A trait for types that can be created from a SQLite value.
|
||||||
pub trait FromSql {
|
pub trait FromSql {
|
||||||
unsafe fn column_result(stmt: *mut ffi::sqlite3_stmt, col: c_int) -> SqliteResult<Self>;
|
unsafe fn column_result(stmt: *mut ffi::sqlite3_stmt, col: c_int) -> SqliteResult<Self>;
|
||||||
}
|
}
|
||||||
@ -75,6 +131,17 @@ impl<T: ToSql> ToSql for Option<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Empty struct that can be used to fill in a query parameter as `NULL`.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// # use rusqlite::{SqliteConnection, SqliteResult};
|
||||||
|
/// # use rusqlite::types::{Null};
|
||||||
|
/// fn insert_null(conn: &SqliteConnection) -> SqliteResult<uint> {
|
||||||
|
/// conn.execute("INSERT INTO people (name) VALUES (?)", &[&Null])
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
pub struct Null;
|
pub struct Null;
|
||||||
|
|
||||||
impl ToSql for Null {
|
impl ToSql for Null {
|
||||||
|
Loading…
Reference in New Issue
Block a user