This commit is contained in:
gwenn 2018-08-16 18:29:46 +02:00
parent 33271764b1
commit 5e9c7bac4e
25 changed files with 527 additions and 385 deletions

View File

@ -18,8 +18,11 @@
//! # use std::path::Path; //! # use std::path::Path;
//! # use std::time; //! # use std::time;
//! //!
//! fn backup_db<P: AsRef<Path>>(src: &Connection, dst: P, progress: fn(backup::Progress)) //! fn backup_db<P: AsRef<Path>>(
//! -> Result<()> { //! src: &Connection,
//! dst: P,
//! progress: fn(backup::Progress),
//! ) -> Result<()> {
//! let mut dst = try!(Connection::open(dst)); //! let mut dst = try!(Connection::open(dst));
//! let backup = try!(backup::Backup::new(src, &mut dst)); //! let backup = try!(backup::Backup::new(src, &mut dst));
//! backup.run_to_completion(5, time::Duration::from_millis(250), Some(progress)) //! backup.run_to_completion(5, time::Duration::from_millis(250), Some(progress))
@ -136,7 +139,8 @@ pub enum StepResult {
/// The backup is complete. /// The backup is complete.
Done, Done,
/// The step was successful but there are still more pages that need to be backed up. /// The step was successful but there are still more pages that need to be
/// backed up.
More, More,
/// The step failed because appropriate locks could not be aquired. This is /// The step failed because appropriate locks could not be aquired. This is

View File

@ -1,14 +1,15 @@
//! Incremental BLOB I/O. //! Incremental BLOB I/O.
//! //!
//! Note that SQLite does not provide API-level access to change the size of a BLOB; that must //! Note that SQLite does not provide API-level access to change the size of a
//! be performed through SQL statements. //! BLOB; that must be performed through SQL statements.
//! //!
//! `Blob` conforms to `std::io::Read`, `std::io::Write`, and `std::io::Seek`, so it plays //! `Blob` conforms to `std::io::Read`, `std::io::Write`, and `std::io::Seek`,
//! nicely with other types that build on these (such as `std::io::BufReader` and //! so it plays nicely with other types that build on these (such as
//! `std::io::BufWriter`). However, you must be careful with the size of the blob. For example, //! `std::io::BufReader` and `std::io::BufWriter`). However, you must be
//! when using a `BufWriter`, the `BufWriter` will accept more data than the `Blob` will allow, //! careful with the size of the blob. For example, when using a `BufWriter`,
//! so make sure to call `flush` and check for errors. (See the unit tests in this module for //! the `BufWriter` will accept more data than the `Blob`
//! an example.) //! will allow, so make sure to call `flush` and check for errors. (See the
//! unit tests in this module for an example.)
//! //!
//! ## Example //! ## Example
//! //!
@ -16,17 +17,21 @@
//! extern crate libsqlite3_sys; //! extern crate libsqlite3_sys;
//! extern crate rusqlite; //! extern crate rusqlite;
//! //!
//! use rusqlite::{Connection, DatabaseName};
//! use rusqlite::blob::ZeroBlob; //! use rusqlite::blob::ZeroBlob;
//! use std::io::{Read, Write, Seek, SeekFrom}; //! use rusqlite::{Connection, DatabaseName};
//! use std::io::{Read, Seek, SeekFrom, Write};
//! //!
//! fn main() { //! fn main() {
//! let db = Connection::open_in_memory().unwrap(); //! let db = Connection::open_in_memory().unwrap();
//! db.execute_batch("CREATE TABLE test (content BLOB);").unwrap(); //! db.execute_batch("CREATE TABLE test (content BLOB);")
//! db.execute("INSERT INTO test (content) VALUES (ZEROBLOB(10))", &[]).unwrap(); //! .unwrap();
//! db.execute("INSERT INTO test (content) VALUES (ZEROBLOB(10))", &[])
//! .unwrap();
//! //!
//! let rowid = db.last_insert_rowid(); //! let rowid = db.last_insert_rowid();
//! let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false).unwrap(); //! let mut blob = db
//! .blob_open(DatabaseName::Main, "test", "content", rowid, false)
//! .unwrap();
//! //!
//! // Make sure to test that the number of bytes written matches what you expect; //! // Make sure to test that the number of bytes written matches what you expect;
//! // if you try to write too much, the data will be truncated to the size of the BLOB. //! // if you try to write too much, the data will be truncated to the size of the BLOB.
@ -39,7 +44,8 @@
//! let bytes_read = blob.read(&mut buf[..]).unwrap(); //! let bytes_read = blob.read(&mut buf[..]).unwrap();
//! assert_eq!(bytes_read, 10); // note we read 10 bytes because the blob has size 10 //! assert_eq!(bytes_read, 10); // note we read 10 bytes because the blob has size 10
//! //!
//! db.execute("INSERT INTO test (content) VALUES (?)", &[&ZeroBlob(64)]).unwrap(); //! db.execute("INSERT INTO test (content) VALUES (?)", &[&ZeroBlob(64)])
//! .unwrap();
//! //!
//! // given a new row ID, we can reopen the blob on that row //! // given a new row ID, we can reopen the blob on that row
//! let rowid = db.last_insert_rowid(); //! let rowid = db.last_insert_rowid();
@ -64,12 +70,14 @@ pub struct Blob<'conn> {
} }
impl Connection { impl Connection {
/// Open a handle to the BLOB located in `row_id`, `column`, `table` in database `db`. /// Open a handle to the BLOB located in `row_id`, `column`, `table` in
/// database `db`.
/// ///
/// # Failure /// # Failure
/// ///
/// Will return `Err` if `db`/`table`/`column` cannot be converted to a C-compatible string /// Will return `Err` if `db`/`table`/`column` cannot be converted to a
/// or if the underlying SQLite BLOB open call fails. /// C-compatible string or if the underlying SQLite BLOB open call
/// fails.
pub fn blob_open<'a>( pub fn blob_open<'a>(
&'a self, &'a self,
db: DatabaseName, db: DatabaseName,
@ -124,8 +132,9 @@ impl<'conn> Blob<'conn> {
/// Close a BLOB handle. /// Close a BLOB handle.
/// ///
/// Calling `close` explicitly is not required (the BLOB will be closed when the /// Calling `close` explicitly is not required (the BLOB will be closed
/// `Blob` is dropped), but it is available so you can get any errors that occur. /// when the `Blob` is dropped), but it is available so you can get any
/// errors that occur.
/// ///
/// # Failure /// # Failure
/// ///
@ -142,8 +151,8 @@ impl<'conn> Blob<'conn> {
} }
impl<'conn> io::Read for Blob<'conn> { impl<'conn> io::Read for Blob<'conn> {
/// Read data from a BLOB incrementally. Will return Ok(0) if the end of the blob /// Read data from a BLOB incrementally. Will return Ok(0) if the end of
/// has been reached. /// the blob has been reached.
/// ///
/// # Failure /// # Failure
/// ///
@ -165,12 +174,13 @@ impl<'conn> io::Read for Blob<'conn> {
} }
impl<'conn> io::Write for Blob<'conn> { impl<'conn> io::Write for Blob<'conn> {
/// Write data into a BLOB incrementally. Will return `Ok(0)` if the end of the blob /// Write data into a BLOB incrementally. Will return `Ok(0)` if the end of
/// has been reached; consider using `Write::write_all(buf)` if you want to get an /// the blob has been reached; consider using `Write::write_all(buf)`
/// error if the entirety of the buffer cannot be written. /// if you want to get an error if the entirety of the buffer cannot be
/// written.
/// ///
/// This function may only modify the contents of the BLOB; it is not possible to increase /// This function may only modify the contents of the BLOB; it is not
/// the size of a BLOB using this API. /// possible to increase the size of a BLOB using this API.
/// ///
/// # Failure /// # Failure
/// ///
@ -230,8 +240,8 @@ impl<'conn> Drop for Blob<'conn> {
/// BLOB of length N that is filled with zeroes. /// BLOB of length N that is filled with zeroes.
/// ///
/// Zeroblobs are intended to serve as placeholders for BLOBs whose content is later written using /// Zeroblobs are intended to serve as placeholders for BLOBs whose content is
/// incremental BLOB I/O routines. /// later written using incremental BLOB I/O routines.
/// ///
/// A negative value for the zeroblob results in a zero-length BLOB. /// A negative value for the zeroblob results in a zero-length BLOB.
#[derive(Copy, Clone)] #[derive(Copy, Clone)]

View File

@ -8,13 +8,17 @@ use ffi;
use {Connection, InnerConnection, Result}; use {Connection, InnerConnection, Result};
impl Connection { impl Connection {
/// Set a busy handler that sleeps for a specified amount of time when a table is locked. /// Set a busy handler that sleeps for a specified amount of time when a
/// The handler will sleep multiple times until at least "ms" milliseconds of sleeping have accumulated. /// table is locked. The handler will sleep multiple times until at
/// least "ms" milliseconds of sleeping have accumulated.
/// ///
/// Calling this routine with an argument equal to zero turns off all busy handlers. /// Calling this routine with an argument equal to zero turns off all busy
/// handlers.
// //
/// There can only be a single busy handler for a particular database connection at any given moment. /// There can only be a single busy handler for a particular database
/// If another busy handler was defined (using `busy_handler`) prior to calling this routine, that other busy handler is cleared. /// connection at any given moment. If another busy handler was defined
/// (using `busy_handler`) prior to calling this routine, that other
/// busy handler is cleared.
pub fn busy_timeout(&self, timeout: Duration) -> Result<()> { pub fn busy_timeout(&self, timeout: Duration) -> Result<()> {
let ms = timeout let ms = timeout
.as_secs() .as_secs()
@ -26,14 +30,21 @@ impl Connection {
/// Register a callback to handle `SQLITE_BUSY` errors. /// Register a callback to handle `SQLITE_BUSY` errors.
/// ///
/// If the busy callback is `None`, then `SQLITE_BUSY is returned immediately upon encountering the lock.` /// If the busy callback is `None`, then `SQLITE_BUSY is returned
/// The argument to the busy handler callback is the number of times that the busy handler has been invoked previously for the same locking event. /// immediately upon encountering the lock.` The argument to the busy
/// If the busy callback returns `false`, then no additional attempts are made to access the database and `SQLITE_BUSY` is returned to the application. /// handler callback is the number of times that the
/// If the callback returns `true`, then another attempt is made to access the database and the cycle repeats. /// busy handler has been invoked previously for the
/// same locking event. If the busy callback returns `false`, then no
/// additional attempts are made to access the
/// database and `SQLITE_BUSY` is returned to the
/// application. If the callback returns `true`, then another attempt
/// is made to access the database and the cycle repeats.
/// ///
/// There can only be a single busy handler defined for each database connection. /// There can only be a single busy handler defined for each database
/// Setting a new busy handler clears any previously set handler. /// connection. Setting a new busy handler clears any previously set
/// Note that calling `busy_timeout()` or evaluating `PRAGMA busy_timeout=N` will change the busy handler and thus clear any previously set busy handler. /// handler. Note that calling `busy_timeout()` or evaluating `PRAGMA
/// busy_timeout=N` will change the busy handler and thus
/// clear any previously set busy handler.
pub fn busy_handler(&self, callback: Option<fn(i32) -> bool>) -> Result<()> { pub fn busy_handler(&self, callback: Option<fn(i32) -> bool>) -> Result<()> {
unsafe extern "C" fn busy_handler_callback(p_arg: *mut c_void, count: c_int) -> c_int { unsafe extern "C" fn busy_handler_callback(p_arg: *mut c_void, count: c_int) -> c_int {
let handler_fn: fn(i32) -> bool = mem::transmute(p_arg); let handler_fn: fn(i32) -> bool = mem::transmute(p_arg);

View File

@ -7,10 +7,10 @@ use std::ops::{Deref, DerefMut};
use {Connection, Result, Statement}; use {Connection, Result, Statement};
impl Connection { impl Connection {
/// Prepare a SQL statement for execution, returning a previously prepared (but /// Prepare a SQL statement for execution, returning a previously prepared
/// not currently in-use) statement if one is available. The returned statement /// (but not currently in-use) statement if one is available. The
/// will be cached for reuse by future calls to `prepare_cached` once it is /// returned statement will be cached for reuse by future calls to
/// dropped. /// `prepare_cached` once it is dropped.
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Connection, Result}; /// # use rusqlite::{Connection, Result};
@ -31,16 +31,17 @@ impl Connection {
/// ///
/// # Failure /// # Failure
/// ///
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the /// Will return `Err` if `sql` cannot be converted to a C-compatible string
/// underlying SQLite call fails. /// or if the underlying SQLite call fails.
pub fn prepare_cached<'a>(&'a self, sql: &str) -> Result<CachedStatement<'a>> { pub fn prepare_cached<'a>(&'a self, sql: &str) -> Result<CachedStatement<'a>> {
self.cache.get(self, sql) self.cache.get(self, sql)
} }
/// Set the maximum number of cached prepared statements this connection will hold. /// Set the maximum number of cached prepared statements this connection
/// By default, a connection will hold a relatively small number of cached statements. /// will hold. By default, a connection will hold a relatively small
/// If you need more, or know that you will not use cached statements, you can set /// number of cached statements. If you need more, or know that you
/// the capacity manually using this method. /// will not use cached statements, you
/// can set the capacity manually using this method.
pub fn set_prepared_statement_cache_capacity(&self, capacity: usize) { pub fn set_prepared_statement_cache_capacity(&self, capacity: usize) {
self.cache.set_capacity(capacity) self.cache.set_capacity(capacity)
} }
@ -95,8 +96,8 @@ impl<'conn> CachedStatement<'conn> {
} }
} }
/// Discard the statement, preventing it from being returned to its `Connection`'s collection /// Discard the statement, preventing it from being returned to its
/// of cached statements. /// `Connection`'s collection of cached statements.
pub fn discard(mut self) { pub fn discard(mut self) {
self.stmt = None; self.stmt = None;
} }
@ -117,8 +118,8 @@ impl StatementCache {
// //
// # Failure // # Failure
// //
// Will return `Err` if no cached statement can be found and the underlying SQLite prepare // Will return `Err` if no cached statement can be found and the underlying
// call fails. // SQLite prepare call fails.
fn get<'conn>( fn get<'conn>(
&'conn self, &'conn self,
conn: &'conn Connection, conn: &'conn Connection,

View File

@ -17,26 +17,29 @@ pub enum Error {
/// An error from an underlying SQLite call. /// An error from an underlying SQLite call.
SqliteFailure(ffi::Error, Option<String>), SqliteFailure(ffi::Error, Option<String>),
/// Error reported when attempting to open a connection when SQLite was configured to /// Error reported when attempting to open a connection when SQLite was
/// allow single-threaded use only. /// configured to allow single-threaded use only.
SqliteSingleThreadedMode, SqliteSingleThreadedMode,
/// Error when the value of a particular column is requested, but it cannot be converted to /// Error when the value of a particular column is requested, but it cannot
/// the requested Rust type. /// be converted to the requested Rust type.
FromSqlConversionFailure(usize, Type, Box<error::Error + Send + Sync>), FromSqlConversionFailure(usize, Type, Box<error::Error + Send + Sync>),
/// Error when SQLite gives us an integral value outside the range of the requested type (e.g., /// Error when SQLite gives us an integral value outside the range of the
/// trying to get the value 1000 into a `u8`). The associated `usize` is the column index, and /// requested type (e.g., trying to get the value 1000 into a `u8`).
/// the associated `i64` is the value returned by SQLite. /// The associated `usize` is the column index,
/// and the associated `i64` is the value returned by SQLite.
IntegralValueOutOfRange(usize, i64), IntegralValueOutOfRange(usize, i64),
/// Error converting a string to UTF-8. /// Error converting a string to UTF-8.
Utf8Error(str::Utf8Error), Utf8Error(str::Utf8Error),
/// Error converting a string to a C-compatible string because it contained an embedded nul. /// Error converting a string to a C-compatible string because it contained
/// an embedded nul.
NulError(::std::ffi::NulError), NulError(::std::ffi::NulError),
/// Error when using SQL named parameters and passing a parameter name not present in the SQL. /// Error when using SQL named parameters and passing a parameter name not
/// present in the SQL.
InvalidParameterName(String), InvalidParameterName(String),
/// Error converting a file path to a string. /// Error converting a file path to a string.
@ -45,31 +48,33 @@ pub enum Error {
/// Error returned when an `execute` call returns rows. /// Error returned when an `execute` call returns rows.
ExecuteReturnedResults, ExecuteReturnedResults,
/// Error when a query that was expected to return at least one row (e.g., for `query_row`) /// Error when a query that was expected to return at least one row (e.g.,
/// did not return any. /// for `query_row`) did not return any.
QueryReturnedNoRows, QueryReturnedNoRows,
/// 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
/// for the statement. /// is out of range for the statement.
InvalidColumnIndex(usize), InvalidColumnIndex(usize),
/// Error when the value of a named column is requested, but no column matches the name /// Error when the value of a named column is requested, but no column
/// for the statement. /// matches the name for the statement.
InvalidColumnName(String), InvalidColumnName(String),
/// Error when the value of a particular column is requested, but the type of the result in /// Error when the value of a particular column is requested, but the type
/// that column cannot be converted to the requested Rust type. /// of the result in that column cannot be converted to the requested
/// Rust type.
InvalidColumnType(usize, Type), InvalidColumnType(usize, Type),
/// Error when a query that was expected to insert one row did not insert any or insert many. /// Error when a query that was expected to insert one row did not insert
/// any or insert many.
StatementChangedRows(usize), StatementChangedRows(usize),
/// Error returned by `functions::Context::get` when the function argument cannot be converted /// Error returned by `functions::Context::get` when the function argument
/// to the requested type. /// cannot be converted to the requested type.
#[cfg(feature = "functions")] #[cfg(feature = "functions")]
InvalidFunctionParameterType(usize, Type), InvalidFunctionParameterType(usize, Type),
/// Error returned by `vtab::Values::get` when the filter argument cannot be converted /// Error returned by `vtab::Values::get` when the filter argument cannot
/// to the requested type. /// be converted to the requested type.
#[cfg(feature = "vtab")] #[cfg(feature = "vtab")]
InvalidFilterParameterType(usize, Type), InvalidFilterParameterType(usize, Type),

View File

@ -2,19 +2,20 @@
//! //!
//! # Example //! # Example
//! //!
//! Adding a `regexp` function to a connection in which compiled regular expressions //! Adding a `regexp` function to a connection in which compiled regular
//! are cached in a `HashMap`. For an alternative implementation that uses SQLite's //! expressions are cached in a `HashMap`. For an alternative implementation
//! [Function Auxilliary Data](https://www.sqlite.org/c3ref/get_auxdata.html) interface //! that uses SQLite's [Function Auxilliary Data](https://www.sqlite.org/c3ref/get_auxdata.html) interface
//! to avoid recompiling regular expressions, see the unit tests for this module. //! to avoid recompiling regular expressions, see the unit tests for this
//! module.
//! //!
//! ```rust //! ```rust
//! extern crate libsqlite3_sys; //! extern crate libsqlite3_sys;
//! extern crate rusqlite; //! extern crate rusqlite;
//! extern crate regex; //! extern crate regex;
//! //!
//! use regex::Regex;
//! use rusqlite::{Connection, Error, Result}; //! use rusqlite::{Connection, Error, Result};
//! use std::collections::HashMap; //! use std::collections::HashMap;
//! use regex::Regex;
//! //!
//! fn add_regexp_function(db: &Connection) -> Result<()> { //! fn add_regexp_function(db: &Connection) -> Result<()> {
//! let mut cached_regexes = HashMap::new(); //! let mut cached_regexes = HashMap::new();
@ -25,12 +26,10 @@
//! use std::collections::hash_map::Entry::{Occupied, Vacant}; //! use std::collections::hash_map::Entry::{Occupied, Vacant};
//! match entry { //! match entry {
//! Occupied(occ) => occ.into_mut(), //! Occupied(occ) => occ.into_mut(),
//! Vacant(vac) => { //! Vacant(vac) => match Regex::new(&regex_s) {
//! match Regex::new(&regex_s) {
//! Ok(r) => vac.insert(r), //! Ok(r) => vac.insert(r),
//! Err(err) => return Err(Error::UserFunctionError(Box::new(err))), //! Err(err) => return Err(Error::UserFunctionError(Box::new(err))),
//! } //! },
//! }
//! } //! }
//! }; //! };
//! //!
@ -43,8 +42,10 @@
//! let db = Connection::open_in_memory().unwrap(); //! let db = Connection::open_in_memory().unwrap();
//! add_regexp_function(&db).unwrap(); //! add_regexp_function(&db).unwrap();
//! //!
//! let is_match: bool = db.query_row("SELECT regexp('[aeiou]*', 'aaaaeeeiii')", &[], //! let is_match: bool = db
//! |row| row.get(0)).unwrap(); //! .query_row("SELECT regexp('[aeiou]*', 'aaaaeeeiii')", &[], |row| {
//! row.get(0)
//! }).unwrap();
//! //!
//! assert!(is_match); //! assert!(is_match);
//! } //! }
@ -64,10 +65,10 @@ use types::{FromSql, FromSqlError, ToSql, ValueRef};
use {str_to_cstring, Connection, Error, InnerConnection, Result}; use {str_to_cstring, Connection, Error, InnerConnection, Result};
unsafe fn report_error(ctx: *mut sqlite3_context, err: &Error) { unsafe fn report_error(ctx: *mut sqlite3_context, err: &Error) {
// Extended constraint error codes were added in SQLite 3.7.16. We don't have an explicit // Extended constraint error codes were added in SQLite 3.7.16. We don't have
// feature check for that, and this doesn't really warrant one. We'll use the extended code // an explicit feature check for that, and this doesn't really warrant one.
// if we're on the bundled version (since it's at least 3.17.0) and the normal constraint // We'll use the extended code if we're on the bundled version (since it's
// error code if not. // at least 3.17.0) and the normal constraint error code if not.
#[cfg(feature = "bundled")] #[cfg(feature = "bundled")]
fn constraint_error_code() -> i32 { fn constraint_error_code() -> i32 {
ffi::SQLITE_CONSTRAINT_FUNCTION ffi::SQLITE_CONSTRAINT_FUNCTION
@ -108,6 +109,7 @@ impl<'a> Context<'a> {
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.args.len() self.args.len()
} }
/// Returns `true` when there is no argument. /// Returns `true` when there is no argument.
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.args.is_empty() self.args.is_empty()
@ -119,7 +121,8 @@ impl<'a> Context<'a> {
/// ///
/// Will panic if `idx` is greater than or equal to `self.len()`. /// Will panic if `idx` is greater than or equal to `self.len()`.
/// ///
/// Will return Err if the underlying SQLite type cannot be converted to a `T`. /// Will return Err if the underlying SQLite type cannot be converted to a
/// `T`.
pub fn get<T: FromSql>(&self, idx: usize) -> Result<T> { pub fn get<T: FromSql>(&self, idx: usize) -> Result<T> {
let arg = self.args[idx]; let arg = self.args[idx];
let value = unsafe { ValueRef::from_value(arg) }; let value = unsafe { ValueRef::from_value(arg) };
@ -169,26 +172,26 @@ impl<'a> Context<'a> {
/// Aggregate is the callback interface for user-defined aggregate function. /// Aggregate is the callback interface for user-defined aggregate function.
/// ///
/// `A` is the type of the aggregation context and `T` is the type of the final result. /// `A` is the type of the aggregation context and `T` is the type of the final
/// Implementations should be stateless. /// result. Implementations should be stateless.
pub trait Aggregate<A, T> pub trait Aggregate<A, T>
where where
T: ToSql, T: ToSql,
{ {
/// Initializes the aggregation context. Will be called prior to the first call /// Initializes the aggregation context. Will be called prior to the first
/// to `step()` to set up the context for an invocation of the function. (Note: /// call to `step()` to set up the context for an invocation of the
/// `init()` will not be called if there are no rows.) /// function. (Note: `init()` will not be called if there are no rows.)
fn init(&self) -> A; fn init(&self) -> A;
/// "step" function called once for each row in an aggregate group. May be called /// "step" function called once for each row in an aggregate group. May be
/// 0 times if there are no rows. /// called 0 times if there are no rows.
fn step(&self, &mut Context, &mut A) -> Result<()>; fn step(&self, &mut Context, &mut A) -> Result<()>;
/// Computes and returns the final result. Will be called exactly once for each /// Computes and returns the final result. Will be called exactly once for
/// invocation of the function. If `step()` was called at least once, will be given /// each invocation of the function. If `step()` was called at least
/// `Some(A)` (the same `A` as was created by `init` and given to `step`); if `step()` /// once, will be given `Some(A)` (the same `A` as was created by
/// was not called (because the function is running against 0 rows), will be given /// `init` and given to `step`); if `step()` was not called (because
/// `None`. /// the function is running against 0 rows), will be given `None`.
fn finalize(&self, Option<A>) -> Result<T>; fn finalize(&self, Option<A>) -> Result<T>;
} }
@ -196,9 +199,9 @@ impl Connection {
/// Attach a user-defined scalar function to this database connection. /// Attach a user-defined scalar function to this database connection.
/// ///
/// `fn_name` is the name the function will be accessible from SQL. /// `fn_name` is the name the function will be accessible from SQL.
/// `n_arg` is the number of arguments to the function. Use `-1` for a variable /// `n_arg` is the number of arguments to the function. Use `-1` for a
/// number. If the function always returns the same value given the same /// variable number. If the function always returns the same value
/// input, `deterministic` should be `true`. /// given the same input, `deterministic` should be `true`.
/// ///
/// The function will remain available until the connection is closed or /// The function will remain available until the connection is closed or
/// until it is explicitly removed via `remove_function`. /// until it is explicitly removed via `remove_function`.

View File

@ -91,7 +91,8 @@ impl From<i32> for Action {
} }
impl Connection { impl Connection {
/// Register a callback function to be invoked whenever a transaction is committed. /// Register a callback function to be invoked whenever a transaction is
/// committed.
/// ///
/// The callback returns `true` to rollback. /// The callback returns `true` to rollback.
pub fn commit_hook<F>(&self, hook: Option<F>) pub fn commit_hook<F>(&self, hook: Option<F>)
@ -101,7 +102,8 @@ impl Connection {
self.db.borrow_mut().commit_hook(hook); self.db.borrow_mut().commit_hook(hook);
} }
/// Register a callback function to be invoked whenever a transaction is committed. /// Register a callback function to be invoked whenever a transaction is
/// committed.
/// ///
/// The callback returns `true` to rollback. /// The callback returns `true` to rollback.
pub fn rollback_hook<F>(&self, hook: Option<F>) pub fn rollback_hook<F>(&self, hook: Option<F>)
@ -116,7 +118,8 @@ impl Connection {
/// ///
/// The callback parameters are: /// The callback parameters are:
/// ///
/// - the type of database update (SQLITE_INSERT, SQLITE_UPDATE or SQLITE_DELETE), /// - the type of database update (SQLITE_INSERT, SQLITE_UPDATE or
/// SQLITE_DELETE),
/// - the name of the database ("main", "temp", ...), /// - the name of the database ("main", "temp", ...),
/// - the name of the table that is updated, /// - the name of the table that is updated,
/// - the ROWID of the row that is updated. /// - the ROWID of the row that is updated.
@ -151,8 +154,9 @@ impl InnerConnection {
} }
} }
// unlike `sqlite3_create_function_v2`, we cannot specify a `xDestroy` with `sqlite3_commit_hook`. // unlike `sqlite3_create_function_v2`, we cannot specify a `xDestroy` with
// so we keep the `xDestroy` function in `InnerConnection.free_boxed_hook`. // `sqlite3_commit_hook`. so we keep the `xDestroy` function in
// `InnerConnection.free_boxed_hook`.
let free_commit_hook = if hook.is_some() { let free_commit_hook = if hook.is_some() {
Some(free_boxed_hook::<F> as fn(*mut c_void)) Some(free_boxed_hook::<F> as fn(*mut c_void))
} else { } else {

View File

@ -1,48 +1,54 @@
//! Rusqlite is an ergonomic wrapper for using SQLite from Rust. It attempts to expose //! Rusqlite is an ergonomic wrapper for using SQLite from Rust. It attempts to
//! an interface similar to [rust-postgres](https://github.com/sfackler/rust-postgres). //! expose an interface similar to [rust-postgres](https://github.com/sfackler/rust-postgres).
//! //!
//! ```rust //! ```rust
//! extern crate rusqlite; //! extern crate rusqlite;
//! extern crate time; //! extern crate time;
//! //!
//! use time::Timespec;
//! use rusqlite::Connection; //! use rusqlite::Connection;
//! use time::Timespec;
//! //!
//! #[derive(Debug)] //! #[derive(Debug)]
//! struct Person { //! struct Person {
//! id: i32, //! id: i32,
//! name: String, //! name: String,
//! time_created: Timespec, //! time_created: Timespec,
//! data: Option<Vec<u8>> //! data: Option<Vec<u8>>,
//! } //! }
//! //!
//! fn main() { //! fn main() {
//! let conn = Connection::open_in_memory().unwrap(); //! let conn = Connection::open_in_memory().unwrap();
//! //!
//! conn.execute("CREATE TABLE person ( //! conn.execute(
//! "CREATE TABLE person (
//! id INTEGER PRIMARY KEY, //! id INTEGER PRIMARY KEY,
//! name TEXT NOT NULL, //! name TEXT NOT NULL,
//! time_created TEXT NOT NULL, //! time_created TEXT NOT NULL,
//! data BLOB //! data BLOB
//! )", &[]).unwrap(); //! )",
//! &[],
//! ).unwrap();
//! let me = Person { //! let me = Person {
//! id: 0, //! id: 0,
//! name: "Steven".to_string(), //! name: "Steven".to_string(),
//! time_created: time::get_time(), //! time_created: time::get_time(),
//! data: None //! data: None,
//! }; //! };
//! conn.execute("INSERT INTO person (name, time_created, data) //! conn.execute(
//! "INSERT INTO person (name, time_created, data)
//! VALUES (?1, ?2, ?3)", //! VALUES (?1, ?2, ?3)",
//! &[&me.name, &me.time_created, &me.data]).unwrap(); //! &[&me.name, &me.time_created, &me.data],
//! ).unwrap();
//! //!
//! let mut stmt = conn.prepare("SELECT id, name, time_created, data FROM person").unwrap(); //! let mut stmt = conn
//! let person_iter = stmt.query_map(&[], |row| { //! .prepare("SELECT id, name, time_created, data FROM person")
//! Person { //! .unwrap();
//! let person_iter = stmt
//! .query_map(&[], |row| Person {
//! id: row.get(0), //! id: row.get(0),
//! name: row.get(1), //! name: row.get(1),
//! time_created: row.get(2), //! time_created: row.get(2),
//! data: row.get(3) //! data: row.get(3),
//! }
//! }).unwrap(); //! }).unwrap();
//! //!
//! for person in person_iter { //! for person in person_iter {
@ -168,8 +174,8 @@ pub enum DatabaseName<'a> {
Attached(&'a str), Attached(&'a str),
} }
// Currently DatabaseName is only used by the backup and blob mods, so hide this (private) // Currently DatabaseName is only used by the backup and blob mods, so hide
// impl to avoid dead code warnings. // this (private) impl to avoid dead code warnings.
#[cfg(any(feature = "backup", feature = "blob"))] #[cfg(any(feature = "backup", feature = "blob"))]
impl<'a> DatabaseName<'a> { impl<'a> DatabaseName<'a> {
fn to_cstring(&self) -> Result<CString> { fn to_cstring(&self) -> Result<CString> {
@ -204,13 +210,15 @@ impl Drop for Connection {
impl Connection { impl Connection {
/// Open a new connection to a SQLite database. /// Open a new connection to a SQLite database.
/// ///
/// `Connection::open(path)` is equivalent to `Connection::open_with_flags(path, /// `Connection::open(path)` is equivalent to
/// OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE)`. /// `Connection::open_with_flags(path,
/// OpenFlags::SQLITE_OPEN_READ_WRITE |
/// OpenFlags::SQLITE_OPEN_CREATE)`.
/// ///
/// # Failure /// # Failure
/// ///
/// Will return `Err` if `path` cannot be converted to a C-compatible string or if the /// Will return `Err` if `path` cannot be converted to a C-compatible
/// underlying SQLite open call fails. /// string or if the underlying SQLite open call fails.
pub fn open<P: AsRef<Path>>(path: P) -> Result<Connection> { pub fn open<P: AsRef<Path>>(path: P) -> Result<Connection> {
let flags = OpenFlags::default(); let flags = OpenFlags::default();
Connection::open_with_flags(path, flags) Connection::open_with_flags(path, flags)
@ -233,8 +241,8 @@ impl Connection {
/// ///
/// # Failure /// # Failure
/// ///
/// Will return `Err` if `path` cannot be converted to a C-compatible string or if the /// Will return `Err` if `path` cannot be converted to a C-compatible
/// underlying SQLite open call fails. /// string or if the underlying SQLite open call fails.
pub fn open_with_flags<P: AsRef<Path>>(path: P, flags: OpenFlags) -> Result<Connection> { pub fn open_with_flags<P: AsRef<Path>>(path: P, flags: OpenFlags) -> Result<Connection> {
let c_path = try!(path_to_cstring(path.as_ref())); let c_path = try!(path_to_cstring(path.as_ref()));
InnerConnection::open_with_flags(&c_path, flags).map(|db| Connection { InnerConnection::open_with_flags(&c_path, flags).map(|db| Connection {
@ -261,7 +269,8 @@ impl Connection {
}) })
} }
/// Convenience method to run multiple SQL statements (that cannot take any parameters). /// 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. /// Uses [sqlite3_exec](http://www.sqlite.org/c3ref/exec.html) under the hood.
/// ///
@ -270,25 +279,27 @@ impl Connection {
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Connection, Result}; /// # use rusqlite::{Connection, Result};
/// fn create_tables(conn: &Connection) -> Result<()> { /// fn create_tables(conn: &Connection) -> Result<()> {
/// conn.execute_batch("BEGIN; /// conn.execute_batch(
/// "BEGIN;
/// CREATE TABLE foo(x INTEGER); /// CREATE TABLE foo(x INTEGER);
/// CREATE TABLE bar(y TEXT); /// CREATE TABLE bar(y TEXT);
/// COMMIT;") /// COMMIT;",
/// )
/// } /// }
/// ``` /// ```
/// ///
/// # Failure /// # Failure
/// ///
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the /// Will return `Err` if `sql` cannot be converted to a C-compatible string
/// underlying SQLite call fails. /// or if the underlying SQLite call fails.
pub fn execute_batch(&self, sql: &str) -> Result<()> { pub fn execute_batch(&self, sql: &str) -> Result<()> {
self.db.borrow_mut().execute_batch(sql) self.db.borrow_mut().execute_batch(sql)
} }
/// Convenience method to prepare and execute a single SQL statement. /// 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 /// On success, returns the number of rows that were changed or inserted or
/// `sqlite3_changes`). /// deleted (via `sqlite3_changes`).
/// ///
/// ## Example /// ## Example
/// ///
@ -304,30 +315,34 @@ impl Connection {
/// ///
/// # Failure /// # Failure
/// ///
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the /// Will return `Err` if `sql` cannot be converted to a C-compatible string
/// underlying SQLite call fails. /// or if the underlying SQLite call fails.
pub fn execute(&self, sql: &str, params: &[&ToSql]) -> Result<usize> { pub fn execute(&self, sql: &str, params: &[&ToSql]) -> Result<usize> {
self.prepare(sql).and_then(|mut stmt| stmt.execute(params)) self.prepare(sql).and_then(|mut stmt| stmt.execute(params))
} }
/// Convenience method to prepare and execute a single SQL statement with named parameter(s). /// Convenience method to prepare and execute a single SQL statement with
/// named parameter(s).
/// ///
/// On success, returns the number of rows that were changed or inserted or deleted (via /// On success, returns the number of rows that were changed or inserted or
/// `sqlite3_changes`). /// deleted (via `sqlite3_changes`).
/// ///
/// ## Example /// ## Example
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Connection, Result}; /// # use rusqlite::{Connection, Result};
/// fn insert(conn: &Connection) -> Result<usize> { /// fn insert(conn: &Connection) -> Result<usize> {
/// conn.execute_named("INSERT INTO test (name) VALUES (:name)", &[(":name", &"one")]) /// conn.execute_named(
/// "INSERT INTO test (name) VALUES (:name)",
/// &[(":name", &"one")],
/// )
/// } /// }
/// ``` /// ```
/// ///
/// # Failure /// # Failure
/// ///
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the /// Will return `Err` if `sql` cannot be converted to a C-compatible string
/// underlying SQLite call fails. /// or if the underlying SQLite call fails.
pub fn execute_named(&self, sql: &str, params: &[(&str, &ToSql)]) -> Result<usize> { pub fn execute_named(&self, sql: &str, params: &[(&str, &ToSql)]) -> Result<usize> {
self.prepare(sql) self.prepare(sql)
.and_then(|mut stmt| stmt.execute_named(params)) .and_then(|mut stmt| stmt.execute_named(params))
@ -341,25 +356,29 @@ impl Connection {
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. /// Convenience method to execute a query that is expected to return a
/// single row.
/// ///
/// ## Example /// ## Example
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Result,Connection}; /// # use rusqlite::{Result,Connection};
/// fn preferred_locale(conn: &Connection) -> Result<String> { /// fn preferred_locale(conn: &Connection) -> Result<String> {
/// conn.query_row("SELECT value FROM preferences WHERE name='locale'", &[], |row| { /// conn.query_row(
/// row.get(0) /// "SELECT value FROM preferences WHERE name='locale'",
/// }) /// &[],
/// |row| row.get(0),
/// )
/// } /// }
/// ``` /// ```
/// ///
/// If the query returns more than one row, all rows except the first are ignored. /// If the query returns more than one row, all rows except the first are
/// ignored.
/// ///
/// # Failure /// # Failure
/// ///
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the /// Will return `Err` if `sql` cannot be converted to a C-compatible string
/// underlying SQLite call fails. /// or if the underlying SQLite call fails.
pub fn query_row<T, F>(&self, sql: &str, params: &[&ToSql], f: F) -> Result<T> pub fn query_row<T, F>(&self, sql: &str, params: &[&ToSql], f: F) -> Result<T>
where where
F: FnOnce(&Row) -> T, F: FnOnce(&Row) -> T,
@ -368,15 +387,16 @@ impl Connection {
stmt.query_row(params, f) stmt.query_row(params, f)
} }
/// Convenience method to execute a query with named parameter(s) that is expected to return /// Convenience method to execute a query with named parameter(s) that is
/// a single row. /// expected to return a single row.
/// ///
/// If the query returns more than one row, all rows except the first are ignored. /// If the query returns more than one row, all rows except the first are
/// ignored.
/// ///
/// # Failure /// # Failure
/// ///
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the /// Will return `Err` if `sql` cannot be converted to a C-compatible string
/// underlying SQLite call fails. /// or if the underlying SQLite call fails.
pub fn query_row_named<T, F>(&self, sql: &str, params: &[(&str, &ToSql)], f: F) -> Result<T> pub fn query_row_named<T, F>(&self, sql: &str, params: &[(&str, &ToSql)], f: F) -> Result<T>
where where
F: FnOnce(&Row) -> T, F: FnOnce(&Row) -> T,
@ -387,29 +407,31 @@ impl Connection {
rows.get_expected_row().map(|r| f(&r)) rows.get_expected_row().map(|r| f(&r))
} }
/// Convenience method to execute a query that is expected to return a single row, /// Convenience method to execute a query that is expected to return a
/// and execute a mapping via `f` on that returned row with the possibility of failure. /// single row, and execute a mapping via `f` on that returned row with
/// The `Result` type of `f` must implement `std::convert::From<Error>`. /// the possibility of failure. The `Result` type of `f` must implement
/// `std::convert::From<Error>`.
/// ///
/// ## Example /// ## Example
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Result,Connection}; /// # use rusqlite::{Result,Connection};
/// fn preferred_locale(conn: &Connection) -> Result<String> { /// fn preferred_locale(conn: &Connection) -> Result<String> {
/// conn.query_row_and_then("SELECT value FROM preferences WHERE name='locale'", /// conn.query_row_and_then(
/// "SELECT value FROM preferences WHERE name='locale'",
/// &[], /// &[],
/// |row| { /// |row| row.get_checked(0),
/// row.get_checked(0) /// )
/// })
/// } /// }
/// ``` /// ```
/// ///
/// If the query returns more than one row, all rows except the first are ignored. /// If the query returns more than one row, all rows except the first are
/// ignored.
/// ///
/// # Failure /// # Failure
/// ///
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the /// Will return `Err` if `sql` cannot be converted to a C-compatible string
/// underlying SQLite call fails. /// or if the underlying SQLite call fails.
pub fn query_row_and_then<T, E, F>( pub fn query_row_and_then<T, E, F>(
&self, &self,
sql: &str, sql: &str,
@ -426,25 +448,29 @@ impl Connection {
rows.get_expected_row().map_err(E::from).and_then(|r| f(&r)) rows.get_expected_row().map_err(E::from).and_then(|r| f(&r))
} }
/// Convenience method to execute a query that is expected to return a single row. /// Convenience method to execute a query that is expected to return a
/// single row.
/// ///
/// ## Example /// ## Example
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Result,Connection}; /// # use rusqlite::{Result,Connection};
/// fn preferred_locale(conn: &Connection) -> Result<String> { /// fn preferred_locale(conn: &Connection) -> Result<String> {
/// conn.query_row_safe("SELECT value FROM preferences WHERE name='locale'", &[], |row| { /// conn.query_row_safe(
/// row.get(0) /// "SELECT value FROM preferences WHERE name='locale'",
/// }) /// &[],
/// |row| row.get(0),
/// )
/// } /// }
/// ``` /// ```
/// ///
/// If the query returns more than one row, all rows except the first are ignored. /// If the query returns more than one row, all rows except the first are
/// ignored.
/// ///
/// ## Deprecated /// ## Deprecated
/// ///
/// This method should be considered deprecated. Use `query_row` instead, which now /// This method should be considered deprecated. Use `query_row` instead,
/// does exactly the same thing. /// which now does exactly the same thing.
#[deprecated(since = "0.1.0", note = "Use query_row instead")] #[deprecated(since = "0.1.0", note = "Use query_row instead")]
pub fn query_row_safe<T, F>(&self, sql: &str, params: &[&ToSql], f: F) -> Result<T> pub fn query_row_safe<T, F>(&self, sql: &str, params: &[&ToSql], f: F) -> Result<T>
where where
@ -469,17 +495,17 @@ impl Connection {
/// ///
/// # Failure /// # Failure
/// ///
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the /// Will return `Err` if `sql` cannot be converted to a C-compatible string
/// underlying SQLite call fails. /// or if the underlying SQLite call fails.
pub fn prepare<'a>(&'a self, sql: &str) -> Result<Statement<'a>> { pub fn prepare<'a>(&'a self, sql: &str) -> Result<Statement<'a>> {
self.db.borrow_mut().prepare(self, sql) self.db.borrow_mut().prepare(self, sql)
} }
/// Close the SQLite connection. /// Close the SQLite connection.
/// ///
/// This is functionally equivalent to the `Drop` implementation for `Connection` except /// This is functionally equivalent to the `Drop` implementation for
/// that on failure, it returns an error and the connection itself (presumably so closing /// `Connection` except that on failure, it returns an error and the
/// can be attempted again). /// connection itself (presumably so closing can be attempted again).
/// ///
/// # Failure /// # Failure
/// ///
@ -490,8 +516,8 @@ impl Connection {
r.map_err(move |err| (self, err)) r.map_err(move |err| (self, err))
} }
/// Enable loading of SQLite extensions. Strongly consider using `LoadExtensionGuard` /// Enable loading of SQLite extensions. Strongly consider using
/// instead of this function. /// `LoadExtensionGuard` instead of this function.
/// ///
/// ## Example /// ## Example
/// ///
@ -525,12 +551,13 @@ impl Connection {
self.db.borrow_mut().enable_load_extension(0) self.db.borrow_mut().enable_load_extension(0)
} }
/// Load the SQLite extension at `dylib_path`. `dylib_path` is passed through to /// Load the SQLite extension at `dylib_path`. `dylib_path` is passed
/// `sqlite3_load_extension`, which may attempt OS-specific modifications if the file /// through to `sqlite3_load_extension`, which may attempt OS-specific
/// cannot be loaded directly. /// modifications if the file cannot be loaded directly.
/// ///
/// If `entry_point` is `None`, SQLite will attempt to find the entry point. If it is not /// If `entry_point` is `None`, SQLite will attempt to find the entry
/// `None`, the entry point will be passed through to `sqlite3_load_extension`. /// point. If it is not `None`, the entry point will be passed through
/// to `sqlite3_load_extension`.
/// ///
/// ## Example /// ## Example
/// ///
@ -562,10 +589,11 @@ impl Connection {
/// ///
/// # Warning /// # Warning
/// ///
/// You should not need to use this function. If you do need to, please [open an issue /// You should not need to use this function. If you do need to, please
/// on the rusqlite repository](https://github.com/jgallagher/rusqlite/issues) and describe /// [open an issue on the rusqlite repository](https://github.com/jgallagher/rusqlite/issues) and describe
/// your use case. This function is unsafe because it gives you raw access to the SQLite /// your use case. This function is unsafe because it gives you raw access
/// connection, and what you do with it could impact the safety of this `Connection`. /// to the SQLite connection, and what you do with it could impact the
/// safety of this `Connection`.
pub unsafe fn handle(&self) -> *mut ffi::sqlite3 { pub unsafe fn handle(&self) -> *mut ffi::sqlite3 {
self.db.borrow().db() self.db.borrow().db()
} }
@ -644,27 +672,34 @@ static SQLITE_VERSION_CHECK: Once = ONCE_INIT;
static BYPASS_SQLITE_INIT: AtomicBool = ATOMIC_BOOL_INIT; static BYPASS_SQLITE_INIT: AtomicBool = ATOMIC_BOOL_INIT;
static BYPASS_VERSION_CHECK: AtomicBool = ATOMIC_BOOL_INIT; static BYPASS_VERSION_CHECK: AtomicBool = ATOMIC_BOOL_INIT;
/// rusqlite's check for a safe SQLite threading mode requires SQLite 3.7.0 or later. If you are /// rusqlite's check for a safe SQLite threading mode requires SQLite 3.7.0 or
/// running against a SQLite older than that, rusqlite attempts to ensure safety by performing /// later. If you are running against a SQLite older than that, rusqlite
/// configuration and initialization of SQLite itself the first time you attempt to open a /// attempts to ensure safety by performing configuration and initialization of
/// connection. By default, rusqlite panics if that initialization fails, since that could mean /// SQLite itself the first time you
/// SQLite has been initialized in single-thread mode. /// attempt to open a connection. By default, rusqlite panics if that
/// initialization fails, since that could mean SQLite has been initialized in
/// single-thread mode.
/// ///
/// If you are encountering that panic _and_ can ensure that SQLite has been initialized in either /// If you are encountering that panic _and_ can ensure that SQLite has been
/// multi-thread or serialized mode, call this function prior to attempting to open a connection /// initialized in either multi-thread or serialized mode, call this function
/// and rusqlite's initialization process will by skipped. This function is unsafe because if you /// prior to attempting to open a connection and rusqlite's initialization
/// call it and SQLite has actually been configured to run in single-thread mode, you may enounter /// process will by skipped. This
/// memory errors or data corruption or any number of terrible things that should not be possible /// function is unsafe because if you call it and SQLite has actually been
/// when you're using Rust. /// configured to run in single-thread mode,
/// you may enounter memory errors or data corruption or any number of terrible
/// things that should not be possible when you're using Rust.
pub unsafe fn bypass_sqlite_initialization() { pub unsafe fn bypass_sqlite_initialization() {
BYPASS_SQLITE_INIT.store(true, Ordering::Relaxed); BYPASS_SQLITE_INIT.store(true, Ordering::Relaxed);
} }
/// rusqlite performs a one-time check that the runtime SQLite version is at least as new as /// rusqlite performs a one-time check that the runtime SQLite version is at
/// the version of SQLite found when rusqlite was built. Bypassing this check may be dangerous; /// least as new as the version of SQLite found when rusqlite was built.
/// e.g., if you use features of SQLite that are not present in the runtime version. If you are /// Bypassing this check may be dangerous; e.g., if you use features of SQLite
/// sure the runtime version is compatible with the build-time version for your usage, you can /// that are not present in the runtime
/// bypass the version check by calling this function before your first connection attempt. /// version. If you are sure the runtime version is compatible with the
/// build-time version for your usage, you can bypass the version check by
/// calling this function before
/// your first connection attempt.
pub unsafe fn bypass_sqlite_version_check() { pub unsafe fn bypass_sqlite_version_check() {
BYPASS_VERSION_CHECK.store(true, Ordering::Relaxed); BYPASS_VERSION_CHECK.store(true, Ordering::Relaxed);
} }
@ -693,8 +728,8 @@ fn ensure_valid_sqlite_version() {
return; return;
} }
// Check that the runtime version number is compatible with the version number we found at // Check that the runtime version number is compatible with the version number
// build-time. // we found at build-time.
if version_number < ffi::SQLITE_VERSION_NUMBER { if version_number < ffi::SQLITE_VERSION_NUMBER {
panic!( panic!(
"\ "\
@ -716,21 +751,23 @@ fn ensure_safe_sqlite_threading_mode() -> Result<()> {
return Err(Error::SqliteSingleThreadedMode); return Err(Error::SqliteSingleThreadedMode);
} }
// Now we know SQLite is _capable_ of being in Multi-thread of Serialized mode, but it's // Now we know SQLite is _capable_ of being in Multi-thread of Serialized mode,
// possible someone configured it to be in Single-thread mode before calling into us. That // but it's possible someone configured it to be in Single-thread mode
// would mean we're exposing an unsafe API via a safe one (in Rust terminology), which is // before calling into us. That would mean we're exposing an unsafe API via
// no good. We have two options to protect against this, depending on the version of SQLite // a safe one (in Rust terminology), which is no good. We have two options
// we're linked with: // to protect against this, depending on the version of SQLite we're linked
// with:
// //
// 1. If we're on 3.7.0 or later, we can ask SQLite for a mutex and check for the magic value // 1. If we're on 3.7.0 or later, we can ask SQLite for a mutex and check for
// 8. This isn't documented, but it's what SQLite returns for its mutex allocation function // the magic value 8. This isn't documented, but it's what SQLite
// in Single-thread mode. // returns for its mutex allocation function in Single-thread mode.
// 2. If we're prior to SQLite 3.7.0, AFAIK there's no way to check the threading mode. The // 2. If we're prior to SQLite 3.7.0, AFAIK there's no way to check the
// check we perform for >= 3.7.0 will segfault. Instead, we insist on being able to call // threading mode. The check we perform for >= 3.7.0 will segfault.
// sqlite3_config and sqlite3_initialize ourself, ensuring we know the threading mode. This // Instead, we insist on being able to call sqlite3_config and
// will fail if someone else has already initialized SQLite even if they initialized it // sqlite3_initialize ourself, ensuring we know the threading
// safely. That's not ideal either, which is why we expose bypass_sqlite_initialization // mode. This will fail if someone else has already initialized SQLite
// above. // even if they initialized it safely. That's not ideal either, which is
// why we expose bypass_sqlite_initialization above.
if version_number() >= 3_007_000 { if version_number() >= 3_007_000 {
const SQLITE_SINGLETHREADED_MUTEX_MAGIC: usize = 8; const SQLITE_SINGLETHREADED_MUTEX_MAGIC: usize = 8;
let is_singlethreaded = unsafe { let is_singlethreaded = unsafe {
@ -775,6 +812,7 @@ impl InnerConnection {
fn new(db: *mut ffi::sqlite3) -> InnerConnection { fn new(db: *mut ffi::sqlite3) -> InnerConnection {
InnerConnection { db } InnerConnection { db }
} }
#[cfg(feature = "hooks")] #[cfg(feature = "hooks")]
fn new(db: *mut ffi::sqlite3) -> InnerConnection { fn new(db: *mut ffi::sqlite3) -> InnerConnection {
InnerConnection { InnerConnection {
@ -789,8 +827,8 @@ impl InnerConnection {
ensure_valid_sqlite_version(); ensure_valid_sqlite_version();
ensure_safe_sqlite_threading_mode()?; ensure_safe_sqlite_threading_mode()?;
// Replicate the check for sane open flags from SQLite, because the check in SQLite itself // Replicate the check for sane open flags from SQLite, because the check in
// wasn't added until version 3.7.3. // SQLite itself wasn't added until version 3.7.3.
debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_ONLY.bits, 0x02); debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_ONLY.bits, 0x02);
debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_WRITE.bits, 0x04); debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_WRITE.bits, 0x04);
debug_assert_eq!( debug_assert_eq!(
@ -1105,8 +1143,9 @@ mod test {
fn test_close_retry() { fn test_close_retry() {
let db = checked_memory_handle(); let db = checked_memory_handle();
// force the DB to be busy by preparing a statement; this must be done at the FFI // force the DB to be busy by preparing a statement; this must be done at the
// level to allow us to call .close() without dropping the prepared statement first. // FFI level to allow us to call .close() without dropping the prepared
// statement first.
let raw_stmt = { let raw_stmt = {
use super::str_to_cstring; use super::str_to_cstring;
use std::mem; use std::mem;
@ -1129,7 +1168,8 @@ mod test {
raw_stmt raw_stmt
}; };
// now that we have an open statement, trying (and retrying) to close should fail. // now that we have an open statement, trying (and retrying) to close should
// fail.
let (db, _) = db.close().unwrap_err(); let (db, _) = db.close().unwrap_err();
let (db, _) = db.close().unwrap_err(); let (db, _) = db.close().unwrap_err();
let (db, _) = db.close().unwrap_err(); let (db, _) = db.close().unwrap_err();
@ -1439,6 +1479,7 @@ mod test {
fn description(&self) -> &str { fn description(&self) -> &str {
"my custom error" "my custom error"
} }
fn cause(&self) -> Option<&StdError> { fn cause(&self) -> Option<&StdError> {
match *self { match *self {
CustomError::SomeError => None, CustomError::SomeError => None,

View File

@ -22,8 +22,9 @@ pub struct LoadExtensionGuard<'conn> {
} }
impl<'conn> LoadExtensionGuard<'conn> { impl<'conn> LoadExtensionGuard<'conn> {
/// Attempt to enable loading extensions. Loading extensions will be disabled when this /// Attempt to enable loading extensions. Loading extensions will be
/// guard goes out of scope. Cannot be meaningfully nested. /// disabled when this guard goes out of scope. Cannot be meaningfully
/// nested.
pub fn new(conn: &Connection) -> Result<LoadExtensionGuard> { pub fn new(conn: &Connection) -> Result<LoadExtensionGuard> {
conn.load_extension_enable() conn.load_extension_enable()
.map(|_| LoadExtensionGuard { conn }) .map(|_| LoadExtensionGuard { conn })

View File

@ -16,16 +16,17 @@ impl<'stmt> Rows<'stmt> {
} }
} }
/// Attempt to get the next row from the query. Returns `Some(Ok(Row))` if there /// Attempt to get the next row from the query. Returns `Some(Ok(Row))` if
/// is another row, `Some(Err(...))` if there was an error getting the next /// there is another row, `Some(Err(...))` if there was an error
/// row, and `None` if all rows have been retrieved. /// getting the next row, and `None` if all rows have been retrieved.
/// ///
/// ## Note /// ## Note
/// ///
/// This interface is not compatible with Rust's `Iterator` trait, because the /// This interface is not compatible with Rust's `Iterator` trait, because
/// lifetime of the returned row is tied to the lifetime of `self`. This is a /// the lifetime of the returned row is tied to the lifetime of `self`.
/// "streaming iterator". For a more natural interface, consider using `query_map` /// This is a "streaming iterator". For a more natural interface,
/// or `query_and_then` instead, which return types that implement `Iterator`. /// consider using `query_map` or `query_and_then` instead, which
/// return types that implement `Iterator`.
#[cfg_attr(feature = "cargo-clippy", allow(should_implement_trait))] // cannot implement Iterator #[cfg_attr(feature = "cargo-clippy", allow(should_implement_trait))] // cannot implement Iterator
pub fn next<'a>(&'a mut self) -> Option<Result<Row<'a, 'stmt>>> { pub fn next<'a>(&'a mut self) -> Option<Result<Row<'a, 'stmt>>> {
self.stmt.and_then(|stmt| match stmt.step() { self.stmt.and_then(|stmt| match stmt.step() {
@ -135,11 +136,15 @@ impl<'a, 'stmt> Row<'a, 'stmt> {
/// ///
/// ## Failure /// ## Failure
/// ///
/// Panics if calling `row.get_checked(idx)` would return an error, including: /// Panics if calling `row.get_checked(idx)` would return an error,
/// including:
/// ///
/// * If the underlying SQLite column type is not a valid type as a source for `T` /// * If the underlying SQLite column type is not a valid type as a
/// * If the underlying SQLite integral value is outside the range representable by `T` /// source for `T`
/// * If `idx` is outside the range of columns in the returned query /// * If the underlying SQLite integral value is
/// outside the range representable by `T`
/// * If `idx` is outside the range of columns in the
/// returned query
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()
} }
@ -151,11 +156,11 @@ impl<'a, 'stmt> Row<'a, 'stmt> {
/// Returns an `Error::InvalidColumnType` if the underlying SQLite column /// Returns an `Error::InvalidColumnType` if the underlying SQLite column
/// type is not a valid type as a source for `T`. /// type is not a valid type as a source for `T`.
/// ///
/// Returns an `Error::InvalidColumnIndex` if `idx` is outside the valid column range /// Returns an `Error::InvalidColumnIndex` if `idx` is outside the valid
/// for this row. /// column range for this row.
/// ///
/// Returns an `Error::InvalidColumnName` if `idx` is not a valid column name /// Returns an `Error::InvalidColumnName` if `idx` is not a valid column
/// for this row. /// name for this row.
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> {
let idx = try!(idx.idx(self.stmt)); let idx = try!(idx.idx(self.stmt));
let value = self.stmt.value_ref(idx); let value = self.stmt.value_ref(idx);

View File

@ -33,19 +33,21 @@ impl<'conn> Statement<'conn> {
cols cols
} }
/// Return the number of columns in the result set returned by the prepared statement. /// Return the number of columns in the result set returned by the prepared
/// statement.
pub fn column_count(&self) -> usize { pub fn column_count(&self) -> usize {
self.stmt.column_count() self.stmt.column_count()
} }
/// Returns the column index in the result set for a given column name. /// Returns the column index in the result set for a given column name.
/// ///
/// If there is no AS clause then the name of the column is unspecified and may change from one /// If there is no AS clause then the name of the column is unspecified and
/// release of SQLite to the next. /// may change from one release of SQLite to the next.
/// ///
/// # Failure /// # Failure
/// ///
/// Will return an `Error::InvalidColumnName` when there is no column with the specified `name`. /// Will return an `Error::InvalidColumnName` when there is no column with
/// the specified `name`.
pub fn column_index(&self, name: &str) -> Result<usize> { pub fn column_index(&self, name: &str) -> Result<usize> {
let bytes = name.as_bytes(); let bytes = name.as_bytes();
let n = self.column_count(); let n = self.column_count();
@ -59,8 +61,8 @@ impl<'conn> Statement<'conn> {
/// Execute the prepared statement. /// Execute the prepared statement.
/// ///
/// On success, returns the number of rows that were changed or inserted or deleted (via /// On success, returns the number of rows that were changed or inserted or
/// `sqlite3_changes`). /// deleted (via `sqlite3_changes`).
/// ///
/// ## Example /// ## Example
/// ///
@ -78,20 +80,22 @@ impl<'conn> Statement<'conn> {
/// ///
/// # Failure /// # Failure
/// ///
/// Will return `Err` if binding parameters fails, the executed statement returns rows (in /// Will return `Err` if binding parameters fails, the executed statement
/// which case `query` should be used instead), or the underling SQLite call fails. /// returns rows (in which case `query` should be used instead), or the
/// underling SQLite call fails.
pub fn execute(&mut self, params: &[&ToSql]) -> Result<usize> { pub fn execute(&mut self, params: &[&ToSql]) -> Result<usize> {
try!(self.bind_parameters(params)); try!(self.bind_parameters(params));
self.execute_with_bound_parameters() self.execute_with_bound_parameters()
} }
/// Execute the prepared statement with named parameter(s). If any parameters /// Execute the prepared statement with named parameter(s). If any
/// that were in the prepared statement are not included in `params`, they /// parameters that were in the prepared statement are not included in
/// will continue to use the most-recently bound value from a previous call /// `params`, they will continue to use the most-recently bound value
/// to `execute_named`, or `NULL` if they have never been bound. /// from a previous call to `execute_named`, or `NULL` if they have
/// never been bound.
/// ///
/// On success, returns the number of rows that were changed or inserted or deleted (via /// On success, returns the number of rows that were changed or inserted or
/// `sqlite3_changes`). /// deleted (via `sqlite3_changes`).
/// ///
/// ## Example /// ## Example
/// ///
@ -105,8 +109,9 @@ impl<'conn> Statement<'conn> {
/// ///
/// # Failure /// # Failure
/// ///
/// Will return `Err` if binding parameters fails, the executed statement returns rows (in /// Will return `Err` if binding parameters fails, the executed statement
/// which case `query` should be used instead), or the underling SQLite call fails. /// returns rows (in which case `query` should be used instead), or the
/// underling SQLite call fails.
pub fn execute_named(&mut self, params: &[(&str, &ToSql)]) -> Result<usize> { pub fn execute_named(&mut self, params: &[(&str, &ToSql)]) -> Result<usize> {
try!(self.bind_parameters_named(params)); try!(self.bind_parameters_named(params));
self.execute_with_bound_parameters() self.execute_with_bound_parameters()
@ -116,10 +121,11 @@ impl<'conn> Statement<'conn> {
/// ///
/// # Note /// # Note
/// ///
/// This function is a convenience wrapper around `execute()` intended for queries that /// This function is a convenience wrapper around `execute()` intended for
/// insert a single item. It is possible to misuse this function in a way that it cannot /// queries that insert a single item. It is possible to misuse this
/// detect, such as by calling it on a statement which _updates_ a single item rather than /// function in a way that it cannot detect, such as by calling it on a
/// inserting one. Please don't do that. /// statement which _updates_ a single
/// item rather than inserting one. Please don't do that.
/// ///
/// # Failure /// # Failure
/// ///
@ -132,11 +138,12 @@ impl<'conn> Statement<'conn> {
} }
} }
/// Execute the prepared statement, returning a handle to 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 /// Due to lifetime restricts, the rows handle returned by `query` does not
/// implement the `Iterator` trait. Consider using `query_map` or `query_and_then` /// implement the `Iterator` trait. Consider using `query_map` or
/// instead, which do. /// `query_and_then` instead, which do.
/// ///
/// ## Example /// ## Example
/// ///
@ -165,10 +172,11 @@ impl<'conn> Statement<'conn> {
Ok(Rows::new(self)) Ok(Rows::new(self))
} }
/// Execute the prepared statement with named parameter(s), returning a handle for the /// Execute the prepared statement with named parameter(s), returning a
/// resulting rows. If any parameters that were in the prepared statement are not included in /// handle for the resulting rows. If any parameters that were in the
/// `params`, they will continue to use the most-recently bound value from a previous call to /// prepared statement are not included in `params`, they will continue
/// `query_named`, or `NULL` if they have never been bound. /// to use the most-recently bound value from a previous
/// call to `query_named`, or `NULL` if they have never been bound.
/// ///
/// ## Example /// ## Example
/// ///
@ -193,8 +201,8 @@ impl<'conn> Statement<'conn> {
Ok(Rows::new(self)) Ok(Rows::new(self))
} }
/// Executes the prepared statement and maps a function over the resulting rows, returning /// Executes the prepared statement and maps a function over the resulting
/// an iterator over the mapped function results. /// rows, returning an iterator over the mapped function results.
/// ///
/// ## Example /// ## Example
/// ///
@ -224,11 +232,12 @@ impl<'conn> Statement<'conn> {
Ok(MappedRows::new(rows, f)) Ok(MappedRows::new(rows, f))
} }
/// Execute the prepared statement with named parameter(s), returning an iterator over the /// Execute the prepared statement with named parameter(s), returning an
/// result of calling the mapping function over the query's rows. If any parameters that were /// iterator over the result of calling the mapping function over the
/// in the prepared statement are not included in `params`, they will continue to use the /// query's rows. If any parameters that were in the prepared statement
/// most-recently bound value from a previous call to `query_named`, or `NULL` if they have /// are not included in `params`, they will continue to use the
/// never been bound. /// most-recently bound value from a previous call to `query_named`,
/// or `NULL` if they have never been bound.
/// ///
/// ## Example /// ## Example
/// ///
@ -263,8 +272,8 @@ impl<'conn> Statement<'conn> {
} }
/// Executes the prepared statement and maps a function over the resulting /// Executes the prepared statement and maps a function over the resulting
/// rows, where the function returns a `Result` with `Error` type implementing /// rows, where the function returns a `Result` with `Error` type
/// `std::convert::From<Error>` (so errors can be unified). /// implementing `std::convert::From<Error>` (so errors can be unified).
/// ///
/// # Failure /// # Failure
/// ///
@ -282,17 +291,21 @@ impl<'conn> Statement<'conn> {
Ok(AndThenRows::new(rows, f)) Ok(AndThenRows::new(rows, f))
} }
/// Execute the prepared statement with named parameter(s), returning an iterator over the /// Execute the prepared statement with named parameter(s), returning an
/// result of calling the mapping function over the query's rows. If any parameters that were /// iterator over the result of calling the mapping function over the
/// in the prepared statement are not included in `params`, they will continue to use the /// query's rows. If any parameters that were in the prepared statement
/// most-recently bound value from a previous call to `query_named`, or `NULL` if they have /// are not included in
/// never been bound. /// `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 /// ## Example
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Connection, Result}; /// # use rusqlite::{Connection, Result};
/// struct Person { name: String }; /// struct Person {
/// name: String,
/// };
/// ///
/// fn name_to_person(name: String) -> Result<Person> { /// fn name_to_person(name: String) -> Result<Person> {
/// // ... check for valid name /// // ... check for valid name
@ -301,9 +314,8 @@ impl<'conn> Statement<'conn> {
/// ///
/// fn get_names(conn: &Connection) -> Result<Vec<Person>> { /// fn get_names(conn: &Connection) -> Result<Vec<Person>> {
/// let mut stmt = try!(conn.prepare("SELECT name FROM people WHERE id = :id")); /// let mut stmt = try!(conn.prepare("SELECT name FROM people WHERE id = :id"));
/// let rows = try!(stmt.query_and_then_named(&[(":id", &"one")], |row| { /// let rows =
/// name_to_person(row.get(0)) /// try!(stmt.query_and_then_named(&[(":id", &"one")], |row| name_to_person(row.get(0))));
/// }));
/// ///
/// let mut persons = Vec::new(); /// let mut persons = Vec::new();
/// for person_result in rows { /// for person_result in rows {
@ -330,8 +342,8 @@ impl<'conn> Statement<'conn> {
Ok(AndThenRows::new(rows, f)) Ok(AndThenRows::new(rows, f))
} }
/// Return `true` if a query in the SQL statement it executes returns one or more rows /// Return `true` if a query in the SQL statement it executes returns one
/// and `false` if the SQL returns an empty set. /// or more rows and `false` if the SQL returns an empty set.
pub fn exists(&mut self, params: &[&ToSql]) -> Result<bool> { pub fn exists(&mut self, params: &[&ToSql]) -> Result<bool> {
let mut rows = try!(self.query(params)); let mut rows = try!(self.query(params));
let exists = { let exists = {
@ -343,9 +355,11 @@ impl<'conn> Statement<'conn> {
Ok(exists) Ok(exists)
} }
/// Convenience method to execute a query that is expected to return a single row. /// Convenience method to execute a query that is expected to return a
/// single row.
/// ///
/// If the query returns more than one row, all rows except the first are ignored. /// If the query returns more than one row, all rows except the first are
/// ignored.
/// ///
/// # Failure /// # Failure
/// ///
@ -361,8 +375,8 @@ impl<'conn> Statement<'conn> {
/// Consumes the statement. /// Consumes the statement.
/// ///
/// Functionally equivalent to the `Drop` implementation, but allows callers to see any errors /// Functionally equivalent to the `Drop` implementation, but allows
/// that occur. /// callers to see any errors that occur.
/// ///
/// # Failure /// # Failure
/// ///
@ -514,7 +528,8 @@ impl<'conn> Statement<'conn> {
Ok(()) Ok(())
} }
/// Returns a string containing the SQL text of prepared statement with bound parameters expanded. /// Returns a string containing the SQL text of prepared statement with
/// bound parameters expanded.
#[cfg(feature = "bundled")] #[cfg(feature = "bundled")]
pub fn expanded_sql(&self) -> Option<&str> { pub fn expanded_sql(&self) -> Option<&str> {
unsafe { unsafe {

View File

@ -61,12 +61,12 @@ pub fn log(err_code: c_int, msg: &str) {
} }
impl Connection { impl Connection {
/// Register or clear a callback function that can be used for tracing the execution of SQL /// Register or clear a callback function that can be used for tracing the
/// statements. /// execution of SQL statements.
/// ///
/// Prepared statement placeholders are replaced/logged with their assigned values. /// Prepared statement placeholders are replaced/logged with their assigned
/// There can only be a single tracer defined for each database connection. /// values. There can only be a single tracer defined for each database
/// Setting a new tracer clears the old one. /// connection. Setting a new tracer clears the old one.
pub fn trace(&mut self, trace_fn: Option<fn(&str)>) { pub fn trace(&mut self, trace_fn: Option<fn(&str)>) {
unsafe extern "C" fn trace_callback(p_arg: *mut c_void, z_sql: *const c_char) { unsafe extern "C" fn trace_callback(p_arg: *mut c_void, z_sql: *const c_char) {
let trace_fn: fn(&str) = mem::transmute(p_arg); let trace_fn: fn(&str) = mem::transmute(p_arg);
@ -86,11 +86,11 @@ impl Connection {
} }
} }
/// Register or clear a callback function that can be used for profiling the execution of SQL /// Register or clear a callback function that can be used for profiling
/// statements. /// the execution of SQL statements.
/// ///
/// There can only be a single profiler defined for each database connection. /// There can only be a single profiler defined for each database
/// Setting a new profiler clears the old one. /// connection. Setting a new profiler clears the old one.
pub fn profile(&mut self, profile_fn: Option<fn(&str, Duration)>) { pub fn profile(&mut self, profile_fn: Option<fn(&str, Duration)>) {
unsafe extern "C" fn profile_callback( unsafe extern "C" fn profile_callback(
p_arg: *mut c_void, p_arg: *mut c_void,

View File

@ -1,7 +1,8 @@
use std::ops::Deref; use std::ops::Deref;
use {Connection, Result}; use {Connection, Result};
/// Old name for `TransactionBehavior`. `SqliteTransactionBehavior` is deprecated. /// Old name for `TransactionBehavior`. `SqliteTransactionBehavior` is
/// deprecated.
#[deprecated(since = "0.6.0", note = "Use TransactionBehavior instead")] #[deprecated(since = "0.6.0", note = "Use TransactionBehavior instead")]
pub type SqliteTransactionBehavior = TransactionBehavior; pub type SqliteTransactionBehavior = TransactionBehavior;
@ -23,8 +24,8 @@ pub enum DropBehavior {
/// Commit the changes. /// Commit the changes.
Commit, Commit,
/// Do not commit or roll back changes - this will leave the transaction or savepoint /// Do not commit or roll back changes - this will leave the transaction or
/// open, so should be used with care. /// savepoint open, so should be used with care.
Ignore, Ignore,
/// Panic. Used to enforce intentional behavior during development. /// Panic. Used to enforce intentional behavior during development.
@ -39,9 +40,9 @@ pub type SqliteTransaction<'conn> = Transaction<'conn>;
/// ///
/// ## Note /// ## Note
/// ///
/// Transactions will roll back by default. Use `commit` method to explicitly commit the /// Transactions will roll back by default. Use `commit` method to explicitly
/// transaction, or use `set_drop_behavior` to change what happens when the transaction /// commit the transaction, or use `set_drop_behavior` to change what happens
/// is dropped. /// when the transaction is dropped.
/// ///
/// ## Example /// ## Example
/// ///
@ -67,9 +68,9 @@ pub struct Transaction<'conn> {
/// ///
/// ## Note /// ## Note
/// ///
/// Savepoints will roll back by default. Use `commit` method to explicitly commit the /// Savepoints will roll back by default. Use `commit` method to explicitly
/// savepoint, or use `set_drop_behavior` to change what happens when the savepoint /// commit the savepoint, or use `set_drop_behavior` to change what happens
/// is dropped. /// when the savepoint is dropped.
/// ///
/// ## Example /// ## Example
/// ///
@ -95,7 +96,8 @@ pub struct Savepoint<'conn> {
} }
impl<'conn> Transaction<'conn> { impl<'conn> Transaction<'conn> {
/// Begin a new transaction. Cannot be nested; see `savepoint` for nested transactions. /// Begin a new transaction. Cannot be nested; see `savepoint` for nested
/// transactions.
// Even though we don't mutate the connection, we take a `&mut Connection` // Even though we don't mutate the connection, we take a `&mut Connection`
// so as to prevent nested or concurrent transactions on the same // so as to prevent nested or concurrent transactions on the same
// connection. // connection.
@ -116,7 +118,8 @@ impl<'conn> Transaction<'conn> {
/// ///
/// ## Note /// ## Note
/// ///
/// Just like outer level transactions, savepoint transactions rollback by default. /// Just like outer level transactions, savepoint transactions rollback by
/// default.
/// ///
/// ## Example /// ## Example
/// ///
@ -146,12 +149,14 @@ impl<'conn> Transaction<'conn> {
Savepoint::with_depth_and_name(self.conn, 1, name) Savepoint::with_depth_and_name(self.conn, 1, name)
} }
/// Get the current setting for what happens to the transaction when it is dropped. /// Get the current setting for what happens to the transaction when it is
/// dropped.
pub fn drop_behavior(&self) -> DropBehavior { pub fn drop_behavior(&self) -> DropBehavior {
self.drop_behavior self.drop_behavior
} }
/// Configure the transaction to perform the specified action when it is dropped. /// Configure the transaction to perform the specified action when it is
/// dropped.
pub fn set_drop_behavior(&mut self, drop_behavior: DropBehavior) { pub fn set_drop_behavior(&mut self, drop_behavior: DropBehavior) {
self.drop_behavior = drop_behavior self.drop_behavior = drop_behavior
} }
@ -176,11 +181,11 @@ impl<'conn> Transaction<'conn> {
Ok(()) Ok(())
} }
/// Consumes the transaction, committing or rolling back according to the current setting /// Consumes the transaction, committing or rolling back according to the
/// (see `drop_behavior`). /// current setting (see `drop_behavior`).
/// ///
/// Functionally equivalent to the `Drop` implementation, but allows callers to see any /// Functionally equivalent to the `Drop` implementation, but allows
/// errors that occur. /// callers to see any errors that occur.
pub fn finish(mut self) -> Result<()> { pub fn finish(mut self) -> Result<()> {
self.finish_() self.finish_()
} }
@ -255,12 +260,14 @@ impl<'conn> Savepoint<'conn> {
Savepoint::with_depth_and_name(self.conn, self.depth + 1, name) Savepoint::with_depth_and_name(self.conn, self.depth + 1, name)
} }
/// Get the current setting for what happens to the savepoint when it is dropped. /// Get the current setting for what happens to the savepoint when it is
/// dropped.
pub fn drop_behavior(&self) -> DropBehavior { pub fn drop_behavior(&self) -> DropBehavior {
self.drop_behavior self.drop_behavior
} }
/// Configure the savepoint to perform the specified action when it is dropped. /// Configure the savepoint to perform the specified action when it is
/// dropped.
pub fn set_drop_behavior(&mut self, drop_behavior: DropBehavior) { pub fn set_drop_behavior(&mut self, drop_behavior: DropBehavior) {
self.drop_behavior = drop_behavior self.drop_behavior = drop_behavior
} }
@ -280,18 +287,18 @@ impl<'conn> Savepoint<'conn> {
/// ///
/// ## Note /// ## Note
/// ///
/// Unlike `Transaction`s, savepoints remain active after they have been rolled back, /// Unlike `Transaction`s, savepoints remain active after they have been
/// and can be rolled back again or committed. /// rolled back, and can be rolled back again or committed.
pub fn rollback(&mut self) -> Result<()> { pub fn rollback(&mut self) -> Result<()> {
self.conn self.conn
.execute_batch(&format!("ROLLBACK TO {}", self.name)) .execute_batch(&format!("ROLLBACK TO {}", self.name))
} }
/// Consumes the savepoint, committing or rolling back according to the current setting /// Consumes the savepoint, committing or rolling back according to the
/// (see `drop_behavior`). /// current setting (see `drop_behavior`).
/// ///
/// Functionally equivalent to the `Drop` implementation, but allows callers to see any /// Functionally equivalent to the `Drop` implementation, but allows
/// errors that occur. /// callers to see any errors that occur.
pub fn finish(mut self) -> Result<()> { pub fn finish(mut self) -> Result<()> {
self.finish_() self.finish_()
} }
@ -327,8 +334,9 @@ impl<'conn> Drop for Savepoint<'conn> {
impl Connection { impl Connection {
/// Begin a new transaction with the default behavior (DEFERRED). /// 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 /// The transaction defaults to rolling back when it is dropped. If you
/// commit, you must call `commit` or `set_drop_behavior(DropBehavior::Commit)`. /// want the transaction to commit, you must call `commit` or
/// `set_drop_behavior(DropBehavior::Commit)`.
/// ///
/// ## Example /// ## Example
/// ///
@ -369,8 +377,9 @@ impl Connection {
/// Begin a new savepoint with the default behavior (DEFERRED). /// Begin a new savepoint with the default behavior (DEFERRED).
/// ///
/// The savepoint defaults to rolling back when it is dropped. If you want the savepoint to /// The savepoint defaults to rolling back when it is dropped. If you want
/// commit, you must call `commit` or `set_drop_behavior(DropBehavior::Commit)`. /// the savepoint to commit, you must call `commit` or
/// `set_drop_behavior(DropBehavior::Commit)`.
/// ///
/// ## Example /// ## Example
/// ///

View File

@ -53,7 +53,8 @@ impl FromSql for NaiveTime {
} }
} }
/// ISO 8601 combined date and time without timezone => "YYYY-MM-DD HH:MM:SS.SSS" /// ISO 8601 combined date and time without timezone =>
/// "YYYY-MM-DD HH:MM:SS.SSS"
impl ToSql for NaiveDateTime { impl ToSql for NaiveDateTime {
fn to_sql(&self) -> Result<ToSqlOutput> { fn to_sql(&self) -> Result<ToSqlOutput> {
let date_str = self.format("%Y-%m-%dT%H:%M:%S%.f").to_string(); let date_str = self.format("%Y-%m-%dT%H:%M:%S%.f").to_string();
@ -61,8 +62,9 @@ impl ToSql for NaiveDateTime {
} }
} }
/// "YYYY-MM-DD HH:MM:SS"/"YYYY-MM-DD HH:MM:SS.SSS" => ISO 8601 combined date and time /// "YYYY-MM-DD HH:MM:SS"/"YYYY-MM-DD HH:MM:SS.SSS" => ISO 8601 combined date
/// without timezone. ("YYYY-MM-DDTHH:MM:SS"/"YYYY-MM-DDTHH:MM:SS.SSS" also supported) /// and time without timezone. ("YYYY-MM-DDTHH:MM:SS"/"YYYY-MM-DDTHH:MM:SS.SSS"
/// also supported)
impl FromSql for NaiveDateTime { impl FromSql for NaiveDateTime {
fn column_result(value: ValueRef) -> FromSqlResult<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
value.as_str().and_then(|s| { value.as_str().and_then(|s| {
@ -80,7 +82,8 @@ impl FromSql for NaiveDateTime {
} }
} }
/// Date and time with time zone => UTC RFC3339 timestamp ("YYYY-MM-DDTHH:MM:SS.SSS+00:00"). /// Date and time with time zone => UTC RFC3339 timestamp
/// ("YYYY-MM-DDTHH:MM:SS.SSS+00:00").
impl<Tz: TimeZone> ToSql for DateTime<Tz> { impl<Tz: TimeZone> ToSql for DateTime<Tz> {
fn to_sql(&self) -> Result<ToSqlOutput> { fn to_sql(&self) -> Result<ToSqlOutput> {
Ok(ToSqlOutput::from(self.with_timezone(&Utc).to_rfc3339())) Ok(ToSqlOutput::from(self.with_timezone(&Utc).to_rfc3339()))

View File

@ -5,11 +5,12 @@ use std::fmt;
/// Enum listing possible errors from `FromSql` trait. /// Enum listing possible errors from `FromSql` trait.
#[derive(Debug)] #[derive(Debug)]
pub enum FromSqlError { pub enum FromSqlError {
/// Error when an SQLite value is requested, but the type of the result cannot be converted to /// Error when an SQLite value is requested, but the type of the result
/// the requested Rust type. /// cannot be converted to the requested Rust type.
InvalidType, InvalidType,
/// Error when the i64 value returned by SQLite cannot be stored into the requested type. /// Error when the i64 value returned by SQLite cannot be stored into the
/// requested type.
OutOfRange(i64), OutOfRange(i64),
/// An error case available for implementors of the `FromSql` trait. /// An error case available for implementors of the `FromSql` trait.
@ -49,13 +50,15 @@ pub type FromSqlResult<T> = Result<T, FromSqlError>;
/// A trait for types that can be created from a SQLite value. /// A trait for types that can be created from a SQLite value.
/// ///
/// Note that `FromSql` and `ToSql` are defined for most integral types, but not `u64` or `usize`. /// Note that `FromSql` and `ToSql` are defined for most integral types, but
/// This is intentional; SQLite returns integers as signed 64-bit values, which cannot fully /// not `u64` or `usize`. This is intentional; SQLite returns integers as
/// represent the range of these types. Rusqlite would have to decide how to handle negative /// signed 64-bit values, which cannot fully represent the range of these
/// values: return an error or reinterpret as a very large postive numbers, neither of which is /// types. Rusqlite would have to
/// guaranteed to be correct for everyone. Callers can work around this by fetching values as i64 /// decide how to handle negative values: return an error or reinterpret as a
/// and then doing the interpretation themselves or by defining a newtype and implementing /// very large postive numbers, neither of which
/// `FromSql`/`ToSql` for it. /// is guaranteed to be correct for everyone. Callers can work around this by
/// fetching values as i64 and then doing the interpretation themselves or by
/// defining a newtype and implementing `FromSql`/`ToSql` for it.
pub trait FromSql: Sized { pub trait FromSql: Sized {
fn column_result(value: ValueRef) -> FromSqlResult<Self>; fn column_result(value: ValueRef) -> FromSqlResult<Self>;
} }

View File

@ -1,11 +1,11 @@
//! Traits dealing with SQLite data types. //! Traits dealing with SQLite data types.
//! //!
//! SQLite uses a [dynamic type system](https://www.sqlite.org/datatype3.html). Implementations of //! 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 //! the `ToSql` and `FromSql` traits are provided for the basic types that
//! for: //! SQLite provides methods for:
//! //!
//! * Integers (`i32` and `i64`; SQLite uses `i64` internally, so getting an `i32` will truncate //! * Integers (`i32` and `i64`; SQLite uses `i64` internally, so getting an
//! if the value is too large or too small). //! `i32` will truncate if the value is too large or too small).
//! * Reals (`f64`) //! * Reals (`f64`)
//! * Strings (`String` and `&str`) //! * Strings (`String` and `&str`)
//! * Blobs (`Vec<u8>` and `&[u8]`) //! * Blobs (`Vec<u8>` and `&[u8]`)
@ -22,16 +22,18 @@
//! extern crate rusqlite; //! extern crate rusqlite;
//! extern crate time; //! extern crate time;
//! //!
//! use rusqlite::types::{FromSql, FromSqlResult, ValueRef, ToSql, ToSqlOutput}; //! use rusqlite::types::{FromSql, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
//! use rusqlite::{Result}; //! use rusqlite::Result;
//! //!
//! pub struct TimespecSql(pub time::Timespec); //! pub struct TimespecSql(pub time::Timespec);
//! //!
//! impl FromSql for TimespecSql { //! impl FromSql for TimespecSql {
//! fn column_result(value: ValueRef) -> FromSqlResult<Self> { //! fn column_result(value: ValueRef) -> FromSqlResult<Self> {
//! f64::column_result(value).map(|as_f64| { //! f64::column_result(value).map(|as_f64| {
//! TimespecSql(time::Timespec{ sec: as_f64.trunc() as i64, //! TimespecSql(time::Timespec {
//! nsec: (as_f64.fract() * 1.0e9) as i32 }) //! sec: as_f64.trunc() as i64,
//! nsec: (as_f64.fract() * 1.0e9) as i32,
//! })
//! }) //! })
//! } //! }
//! } //! }
@ -48,9 +50,9 @@
//! # fn main() {} //! # fn main() {}
//! ``` //! ```
//! //!
//! `ToSql` and `FromSql` are also implemented for `Option<T>` where `T` implements `ToSql` or //! `ToSql` and `FromSql` are also implemented for `Option<T>` where `T`
//! `FromSql` for the cases where you want to know if a value was NULL (which gets translated to //! implements `ToSql` or `FromSql` for the cases where you want to know if a
//! `None`). //! value was NULL (which gets translated to `None`).
pub use self::from_sql::{FromSql, FromSqlError, FromSqlResult}; pub use self::from_sql::{FromSql, FromSqlError, FromSqlResult};
pub use self::to_sql::{ToSql, ToSqlOutput}; pub use self::to_sql::{ToSql, ToSqlOutput};
@ -77,8 +79,7 @@ mod value_ref;
/// # extern crate rusqlite; /// # extern crate rusqlite;
/// # use rusqlite::{Connection, Result}; /// # use rusqlite::{Connection, Result};
/// # use rusqlite::types::{Null}; /// # use rusqlite::types::{Null};
/// fn main() { /// fn main() {}
/// }
/// fn insert_null(conn: &Connection) -> Result<usize> { /// fn insert_null(conn: &Connection) -> Result<usize> {
/// conn.execute("INSERT INTO people (name) VALUES (?)", &[&Null]) /// conn.execute("INSERT INTO people (name) VALUES (?)", &[&Null])
/// } /// }

View File

@ -4,7 +4,8 @@ use std::borrow::Cow;
use vtab::array::Array; use vtab::array::Array;
use Result; use Result;
/// `ToSqlOutput` represents the possible output types for implementors of the `ToSql` trait. /// `ToSqlOutput` represents the possible output types for implementors of the
/// `ToSql` trait.
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum ToSqlOutput<'a> { pub enum ToSqlOutput<'a> {
/// A borrowed SQLite-representable value. /// A borrowed SQLite-representable value.

View File

@ -32,8 +32,8 @@ impl<'a> ValueRef<'a> {
} }
impl<'a> ValueRef<'a> { impl<'a> ValueRef<'a> {
/// If `self` is case `Integer`, returns the integral value. Otherwise, returns /// If `self` is case `Integer`, returns the integral value. Otherwise,
/// `Err(Error::InvalidColumnType)`. /// returns `Err(Error::InvalidColumnType)`.
pub fn as_i64(&self) -> FromSqlResult<i64> { pub fn as_i64(&self) -> FromSqlResult<i64> {
match *self { match *self {
ValueRef::Integer(i) => Ok(i), ValueRef::Integer(i) => Ok(i),
@ -41,8 +41,8 @@ impl<'a> ValueRef<'a> {
} }
} }
/// If `self` is case `Real`, returns the floating point value. Otherwise, returns /// If `self` is case `Real`, returns the floating point value. Otherwise,
/// `Err(Error::InvalidColumnType)`. /// returns `Err(Error::InvalidColumnType)`.
pub fn as_f64(&self) -> FromSqlResult<f64> { pub fn as_f64(&self) -> FromSqlResult<f64> {
match *self { match *self {
ValueRef::Real(f) => Ok(f), ValueRef::Real(f) => Ok(f),

View File

@ -1,7 +1,8 @@
use ffi; use ffi;
use std::ffi::CStr; use std::ffi::CStr;
/// Returns the SQLite version as an integer; e.g., `3016002` for version 3.16.2. /// Returns the SQLite version as an integer; e.g., `3016002` for version
/// 3.16.2.
/// ///
/// See [`sqlite3_libversion_number()`](https://www.sqlite.org/c3ref/libversion.html). /// See [`sqlite3_libversion_number()`](https://www.sqlite.org/c3ref/libversion.html).
pub fn version_number() -> i32 { pub fn version_number() -> i32 {

View File

@ -120,6 +120,7 @@ impl ArrayTabCursor {
ptr: None, ptr: None,
} }
} }
fn len(&self) -> i64 { fn len(&self) -> i64 {
match self.ptr { match self.ptr {
Some(ref a) => a.len() as i64, Some(ref a) => a.len() as i64,
@ -137,13 +138,16 @@ impl VTabCursor for ArrayTabCursor {
self.row_id = 1; self.row_id = 1;
Ok(()) Ok(())
} }
fn next(&mut self) -> Result<()> { fn next(&mut self) -> Result<()> {
self.row_id += 1; self.row_id += 1;
Ok(()) Ok(())
} }
fn eof(&self) -> bool { fn eof(&self) -> bool {
self.row_id > self.len() self.row_id > self.len()
} }
fn column(&self, ctx: &mut Context, i: c_int) -> Result<()> { fn column(&self, ctx: &mut Context, i: c_int) -> Result<()> {
match i { match i {
CARRAY_COLUMN_POINTER => Ok(()), CARRAY_COLUMN_POINTER => Ok(()),
@ -157,6 +161,7 @@ impl VTabCursor for ArrayTabCursor {
} }
} }
} }
fn rowid(&self) -> Result<i64> { fn rowid(&self) -> Result<i64> {
Ok(self.row_id) Ok(self.row_id)
} }

View File

@ -293,6 +293,7 @@ impl VTabCursor for CSVTabCursor {
self.row_number = 0; self.row_number = 0;
self.next() self.next()
} }
fn next(&mut self) -> Result<()> { fn next(&mut self) -> Result<()> {
{ {
self.eof = self.reader.is_done(); self.eof = self.reader.is_done();
@ -306,9 +307,11 @@ impl VTabCursor for CSVTabCursor {
self.row_number += 1; self.row_number += 1;
Ok(()) Ok(())
} }
fn eof(&self) -> bool { fn eof(&self) -> bool {
self.eof self.eof
} }
fn column(&self, ctx: &mut Context, col: c_int) -> Result<()> { fn column(&self, ctx: &mut Context, col: c_int) -> Result<()> {
if col < 0 || col as usize >= self.cols.len() { if col < 0 || col as usize >= self.cols.len() {
return Err(Error::ModuleError(format!( return Err(Error::ModuleError(format!(
@ -322,6 +325,7 @@ impl VTabCursor for CSVTabCursor {
// TODO Affinity // TODO Affinity
ctx.set_result(&self.cols[col as usize].to_owned()) ctx.set_result(&self.cols[col as usize].to_owned())
} }
fn rowid(&self) -> Result<i64> { fn rowid(&self) -> Result<i64> {
Ok(self.row_number as i64) Ok(self.row_number as i64)
} }

View File

@ -278,12 +278,14 @@ impl IndexInfo {
(*self.0).idxNum = idx_num; (*self.0).idxNum = idx_num;
} }
} }
/// True if output is already ordered /// True if output is already ordered
pub fn set_order_by_consumed(&mut self, order_by_consumed: bool) { pub fn set_order_by_consumed(&mut self, order_by_consumed: bool) {
unsafe { unsafe {
(*self.0).orderByConsumed = if order_by_consumed { 1 } else { 0 }; (*self.0).orderByConsumed = if order_by_consumed { 1 } else { 0 };
} }
} }
/// Estimated cost of using this index /// Estimated cost of using this index
pub fn set_estimated_cost(&mut self, estimated_ost: f64) { pub fn set_estimated_cost(&mut self, estimated_ost: f64) {
unsafe { unsafe {
@ -329,10 +331,12 @@ impl<'a> IndexConstraint<'a> {
pub fn column(&self) -> c_int { pub fn column(&self) -> c_int {
self.0.iColumn self.0.iColumn
} }
/// Constraint operator /// Constraint operator
pub fn operator(&self) -> IndexConstraintOp { pub fn operator(&self) -> IndexConstraintOp {
IndexConstraintOp::from_bits_truncate(self.0.op) IndexConstraintOp::from_bits_truncate(self.0.op)
} }
/// True if this constraint is usable /// True if this constraint is usable
pub fn is_usable(&self) -> bool { pub fn is_usable(&self) -> bool {
self.0.usable != 0 self.0.usable != 0
@ -347,6 +351,7 @@ impl<'a> IndexConstraintUsage<'a> {
pub fn set_argv_index(&mut self, argv_index: c_int) { pub fn set_argv_index(&mut self, argv_index: c_int) {
self.0.argvIndex = argv_index; self.0.argvIndex = argv_index;
} }
/// if `omit`, do not code a test for this constraint /// if `omit`, do not code a test for this constraint
pub fn set_omit(&mut self, omit: bool) { pub fn set_omit(&mut self, omit: bool) {
self.0.omit = if omit { 1 } else { 0 }; self.0.omit = if omit { 1 } else { 0 };
@ -377,6 +382,7 @@ impl<'a> OrderBy<'a> {
pub fn column(&self) -> c_int { pub fn column(&self) -> c_int {
self.0.iColumn self.0.iColumn
} }
/// True for DESC. False for ASC. /// True for DESC. False for ASC.
pub fn is_order_by_desc(&self) -> bool { pub fn is_order_by_desc(&self) -> bool {
self.0.desc != 0 self.0.desc != 0
@ -483,8 +489,8 @@ impl<'a> Values<'a> {
} }
impl<'a> IntoIterator for &'a Values<'a> { impl<'a> IntoIterator for &'a Values<'a> {
type Item = ValueRef<'a>;
type IntoIter = ValueIter<'a>; type IntoIter = ValueIter<'a>;
type Item = ValueRef<'a>;
fn into_iter(self) -> ValueIter<'a> { fn into_iter(self) -> ValueIter<'a> {
self.iter() self.iter()

View File

@ -227,6 +227,7 @@ impl VTabCursor for SeriesTabCursor {
self.row_id = 1; self.row_id = 1;
Ok(()) Ok(())
} }
fn next(&mut self) -> Result<()> { fn next(&mut self) -> Result<()> {
if self.is_desc { if self.is_desc {
self.value -= self.step; self.value -= self.step;
@ -236,6 +237,7 @@ impl VTabCursor for SeriesTabCursor {
self.row_id += 1; self.row_id += 1;
Ok(()) Ok(())
} }
fn eof(&self) -> bool { fn eof(&self) -> bool {
if self.is_desc { if self.is_desc {
self.value < self.min_value self.value < self.min_value
@ -243,6 +245,7 @@ impl VTabCursor for SeriesTabCursor {
self.value > self.max_value self.value > self.max_value
} }
} }
fn column(&self, ctx: &mut Context, i: c_int) -> Result<()> { fn column(&self, ctx: &mut Context, i: c_int) -> Result<()> {
let x = match i { let x = match i {
SERIES_COLUMN_START => self.min_value, SERIES_COLUMN_START => self.min_value,
@ -252,6 +255,7 @@ impl VTabCursor for SeriesTabCursor {
}; };
ctx.set_result(&x) ctx.set_result(&x)
} }
fn rowid(&self) -> Result<i64> { fn rowid(&self) -> Result<i64> {
Ok(self.row_id) Ok(self.row_id)
} }

View File

@ -1,5 +1,6 @@
//! This file contains unit tests for `rusqlite::trace::config_log`. This function affects //! This file contains unit tests for `rusqlite::trace::config_log`. This
//! SQLite process-wide and so is not safe to run as a normal #[test] in the library. //! function affects SQLite process-wide and so is not safe to run as a normal
//! #[test] in the library.
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
#[macro_use] #[macro_use]

View File

@ -65,16 +65,20 @@ fn test_dummy_module() {
self.row_id = 1; self.row_id = 1;
Ok(()) Ok(())
} }
fn next(&mut self) -> Result<()> { fn next(&mut self) -> Result<()> {
self.row_id += 1; self.row_id += 1;
Ok(()) Ok(())
} }
fn eof(&self) -> bool { fn eof(&self) -> bool {
self.row_id > 1 self.row_id > 1
} }
fn column(&self, ctx: &mut Context, _: c_int) -> Result<()> { fn column(&self, ctx: &mut Context, _: c_int) -> Result<()> {
ctx.set_result(&self.row_id) ctx.set_result(&self.row_id)
} }
fn rowid(&self) -> Result<i64> { fn rowid(&self) -> Result<i64> {
Ok(self.row_id) Ok(self.row_id)
} }