mirror of
				https://github.com/isar/rusqlite.git
				synced 2025-10-31 05:48:56 +08:00 
			
		
		
		
	Add documentation
This commit is contained in:
		
							
								
								
									
										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 { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user