diff options
Diffstat (limited to 'third_party/rust/rusqlite/src/lib.rs')
-rw-r--r-- | third_party/rust/rusqlite/src/lib.rs | 2127 |
1 files changed, 2127 insertions, 0 deletions
diff --git a/third_party/rust/rusqlite/src/lib.rs b/third_party/rust/rusqlite/src/lib.rs new file mode 100644 index 0000000000..89f133e07f --- /dev/null +++ b/third_party/rust/rusqlite/src/lib.rs @@ -0,0 +1,2127 @@ +//! Rusqlite is an ergonomic wrapper for using SQLite from Rust. +//! +//! Historically, the API was based on the one from +//! [`rust-postgres`](https://github.com/sfackler/rust-postgres). However, the +//! two have diverged in many ways, and no compatibility between the two is +//! intended. +//! +//! ```rust +//! use rusqlite::{params, Connection, Result}; +//! +//! #[derive(Debug)] +//! struct Person { +//! id: i32, +//! name: String, +//! data: Option<Vec<u8>>, +//! } +//! +//! fn main() -> Result<()> { +//! let conn = Connection::open_in_memory()?; +//! +//! conn.execute( +//! "CREATE TABLE person ( +//! id INTEGER PRIMARY KEY, +//! name TEXT NOT NULL, +//! data BLOB +//! )", +//! (), // empty list of parameters. +//! )?; +//! let me = Person { +//! id: 0, +//! name: "Steven".to_string(), +//! data: None, +//! }; +//! conn.execute( +//! "INSERT INTO person (name, data) VALUES (?1, ?2)", +//! (&me.name, &me.data), +//! )?; +//! +//! let mut stmt = conn.prepare("SELECT id, name, data FROM person")?; +//! let person_iter = stmt.query_map([], |row| { +//! Ok(Person { +//! id: row.get(0)?, +//! name: row.get(1)?, +//! data: row.get(2)?, +//! }) +//! })?; +//! +//! for person in person_iter { +//! println!("Found person {:?}", person.unwrap()); +//! } +//! Ok(()) +//! } +//! ``` +#![warn(missing_docs)] +#![cfg_attr(docsrs, feature(doc_cfg))] + +pub use libsqlite3_sys as ffi; + +use std::cell::RefCell; +use std::default::Default; +use std::ffi::{CStr, CString}; +use std::fmt; +use std::os::raw::{c_char, c_int}; + +use std::path::{Path, PathBuf}; +use std::result; +use std::str; +use std::sync::atomic::Ordering; +use std::sync::{Arc, Mutex}; + +use crate::cache::StatementCache; +use crate::inner_connection::{InnerConnection, BYPASS_SQLITE_INIT}; +use crate::raw_statement::RawStatement; +use crate::types::ValueRef; + +pub use crate::cache::CachedStatement; +pub use crate::column::Column; +pub use crate::error::Error; +pub use crate::ffi::ErrorCode; +#[cfg(feature = "load_extension")] +pub use crate::load_extension_guard::LoadExtensionGuard; +pub use crate::params::{params_from_iter, Params, ParamsFromIter}; +pub use crate::row::{AndThenRows, Map, MappedRows, Row, RowIndex, Rows}; +pub use crate::statement::{Statement, StatementStatus}; +pub use crate::transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior}; +pub use crate::types::ToSql; +pub use crate::version::*; + +mod error; + +#[cfg(feature = "backup")] +#[cfg_attr(docsrs, doc(cfg(feature = "backup")))] +pub mod backup; +#[cfg(feature = "blob")] +#[cfg_attr(docsrs, doc(cfg(feature = "blob")))] +pub mod blob; +mod busy; +mod cache; +#[cfg(feature = "collation")] +#[cfg_attr(docsrs, doc(cfg(feature = "collation")))] +mod collation; +mod column; +pub mod config; +#[cfg(any(feature = "functions", feature = "vtab"))] +mod context; +#[cfg(feature = "functions")] +#[cfg_attr(docsrs, doc(cfg(feature = "functions")))] +pub mod functions; +#[cfg(feature = "hooks")] +#[cfg_attr(docsrs, doc(cfg(feature = "hooks")))] +pub mod hooks; +mod inner_connection; +#[cfg(feature = "limits")] +#[cfg_attr(docsrs, doc(cfg(feature = "limits")))] +pub mod limits; +#[cfg(feature = "load_extension")] +mod load_extension_guard; +mod params; +mod pragma; +mod raw_statement; +mod row; +#[cfg(feature = "session")] +#[cfg_attr(docsrs, doc(cfg(feature = "session")))] +pub mod session; +mod statement; +#[cfg(feature = "trace")] +#[cfg_attr(docsrs, doc(cfg(feature = "trace")))] +pub mod trace; +mod transaction; +pub mod types; +#[cfg(feature = "unlock_notify")] +mod unlock_notify; +mod version; +#[cfg(feature = "vtab")] +#[cfg_attr(docsrs, doc(cfg(feature = "vtab")))] +pub mod vtab; + +pub(crate) mod util; +pub(crate) use util::SmallCString; + +// Number of cached prepared statements we'll hold on to. +const STATEMENT_CACHE_DEFAULT_CAPACITY: usize = 16; +/// To be used when your statement has no [parameter][sqlite-varparam]. +/// +/// [sqlite-varparam]: https://sqlite.org/lang_expr.html#varparam +/// +/// This is deprecated in favor of using an empty array literal. +#[deprecated = "Use an empty array instead; `stmt.execute(NO_PARAMS)` => `stmt.execute([])`"] +pub const NO_PARAMS: &[&dyn ToSql] = &[]; + +/// A macro making it more convenient to longer lists of +/// parameters as a `&[&dyn ToSql]`. +/// +/// # Example +/// +/// ```rust,no_run +/// # use rusqlite::{Result, Connection, params}; +/// +/// struct Person { +/// name: String, +/// age_in_years: u8, +/// data: Option<Vec<u8>>, +/// } +/// +/// fn add_person(conn: &Connection, person: &Person) -> Result<()> { +/// conn.execute( +/// "INSERT INTO person(name, age_in_years, data) VALUES (?1, ?2, ?3)", +/// params![person.name, person.age_in_years, person.data], +/// )?; +/// Ok(()) +/// } +/// ``` +#[macro_export] +macro_rules! params { + () => { + &[] as &[&dyn $crate::ToSql] + }; + ($($param:expr),+ $(,)?) => { + &[$(&$param as &dyn $crate::ToSql),+] as &[&dyn $crate::ToSql] + }; +} + +/// A macro making it more convenient to pass lists of named parameters +/// as a `&[(&str, &dyn ToSql)]`. +/// +/// # Example +/// +/// ```rust,no_run +/// # use rusqlite::{Result, Connection, named_params}; +/// +/// struct Person { +/// name: String, +/// age_in_years: u8, +/// data: Option<Vec<u8>>, +/// } +/// +/// fn add_person(conn: &Connection, person: &Person) -> Result<()> { +/// conn.execute( +/// "INSERT INTO person (name, age_in_years, data) +/// VALUES (:name, :age, :data)", +/// named_params! { +/// ":name": person.name, +/// ":age": person.age_in_years, +/// ":data": person.data, +/// }, +/// )?; +/// Ok(()) +/// } +/// ``` +#[macro_export] +macro_rules! named_params { + () => { + &[] as &[(&str, &dyn $crate::ToSql)] + }; + // Note: It's a lot more work to support this as part of the same macro as + // `params!`, unfortunately. + ($($param_name:literal: $param_val:expr),+ $(,)?) => { + &[$(($param_name, &$param_val as &dyn $crate::ToSql)),+] as &[(&str, &dyn $crate::ToSql)] + }; +} + +/// A typedef of the result returned by many methods. +pub type Result<T, E = Error> = result::Result<T, E>; + +/// See the [method documentation](#tymethod.optional). +pub trait OptionalExtension<T> { + /// Converts a `Result<T>` into a `Result<Option<T>>`. + /// + /// By default, Rusqlite treats 0 rows being returned from a query that is + /// expected to return 1 row as an error. This method will + /// handle that error, and give you back an `Option<T>` instead. + fn optional(self) -> Result<Option<T>>; +} + +impl<T> OptionalExtension<T> for Result<T> { + fn optional(self) -> Result<Option<T>> { + match self { + Ok(value) => Ok(Some(value)), + Err(Error::QueryReturnedNoRows) => Ok(None), + Err(e) => Err(e), + } + } +} + +unsafe fn errmsg_to_string(errmsg: *const c_char) -> String { + let c_slice = CStr::from_ptr(errmsg).to_bytes(); + String::from_utf8_lossy(c_slice).into_owned() +} + +fn str_to_cstring(s: &str) -> Result<SmallCString> { + Ok(SmallCString::new(s)?) +} + +/// Returns `Ok((string ptr, len as c_int, SQLITE_STATIC | SQLITE_TRANSIENT))` +/// normally. +/// Returns error if the string is too large for sqlite. +/// The `sqlite3_destructor_type` item is always `SQLITE_TRANSIENT` unless +/// the string was empty (in which case it's `SQLITE_STATIC`, and the ptr is +/// static). +fn str_for_sqlite(s: &[u8]) -> Result<(*const c_char, c_int, ffi::sqlite3_destructor_type)> { + let len = len_as_c_int(s.len())?; + let (ptr, dtor_info) = if len != 0 { + (s.as_ptr().cast::<c_char>(), ffi::SQLITE_TRANSIENT()) + } else { + // Return a pointer guaranteed to live forever + ("".as_ptr().cast::<c_char>(), ffi::SQLITE_STATIC()) + }; + Ok((ptr, len, dtor_info)) +} + +// Helper to cast to c_int safely, returning the correct error type if the cast +// failed. +fn len_as_c_int(len: usize) -> Result<c_int> { + if len >= (c_int::MAX as usize) { + Err(Error::SqliteFailure( + ffi::Error::new(ffi::SQLITE_TOOBIG), + None, + )) + } else { + Ok(len as c_int) + } +} + +#[cfg(unix)] +fn path_to_cstring(p: &Path) -> Result<CString> { + use std::os::unix::ffi::OsStrExt; + Ok(CString::new(p.as_os_str().as_bytes())?) +} + +#[cfg(not(unix))] +fn path_to_cstring(p: &Path) -> Result<CString> { + let s = p.to_str().ok_or_else(|| Error::InvalidPath(p.to_owned()))?; + Ok(CString::new(s)?) +} + +/// Name for a database within a SQLite connection. +#[derive(Copy, Clone, Debug)] +pub enum DatabaseName<'a> { + /// The main database. + Main, + + /// The temporary database (e.g., any "CREATE TEMPORARY TABLE" tables). + Temp, + + /// A database that has been attached via "ATTACH DATABASE ...". + Attached(&'a str), +} + +/// Shorthand for [`DatabaseName::Main`]. +pub const MAIN_DB: DatabaseName<'static> = DatabaseName::Main; + +/// Shorthand for [`DatabaseName::Temp`]. +pub const TEMP_DB: DatabaseName<'static> = DatabaseName::Temp; + +// Currently DatabaseName is only used by the backup and blob mods, so hide +// this (private) impl to avoid dead code warnings. +#[cfg(any( + feature = "backup", + feature = "blob", + feature = "session", + feature = "modern_sqlite" +))] +impl DatabaseName<'_> { + #[inline] + fn as_cstring(&self) -> Result<SmallCString> { + use self::DatabaseName::{Attached, Main, Temp}; + match *self { + Main => str_to_cstring("main"), + Temp => str_to_cstring("temp"), + Attached(s) => str_to_cstring(s), + } + } +} + +/// A connection to a SQLite database. +pub struct Connection { + db: RefCell<InnerConnection>, + cache: StatementCache, + path: Option<PathBuf>, +} + +unsafe impl Send for Connection {} + +impl Drop for Connection { + #[inline] + fn drop(&mut self) { + self.flush_prepared_statement_cache(); + } +} + +impl Connection { + /// Open a new connection to a SQLite database. If a database does not exist + /// at the path, one is created. + /// + /// ```rust,no_run + /// # use rusqlite::{Connection, Result}; + /// fn open_my_db() -> Result<()> { + /// let path = "./my_db.db3"; + /// let db = Connection::open(path)?; + /// // Use the database somehow... + /// println!("{}", db.is_autocommit()); + /// Ok(()) + /// } + /// ``` + /// + /// # Flags + /// + /// `Connection::open(path)` is equivalent to using + /// [`Connection::open_with_flags`] with the default [`OpenFlags`]. That is, + /// it's equivalent to: + /// + /// ```ignore + /// Connection::open_with_flags( + /// path, + /// OpenFlags::SQLITE_OPEN_READ_WRITE + /// | OpenFlags::SQLITE_OPEN_CREATE + /// | OpenFlags::SQLITE_OPEN_URI + /// | OpenFlags::SQLITE_OPEN_NO_MUTEX, + /// ) + /// ``` + /// + /// These flags have the following effects: + /// + /// - Open the database for both reading or writing. + /// - Create the database if one does not exist at the path. + /// - Allow the filename to be interpreted as a URI (see <https://www.sqlite.org/uri.html#uri_filenames_in_sqlite> + /// for details). + /// - Disables the use of a per-connection mutex. + /// + /// Rusqlite enforces thread-safety at compile time, so additional + /// locking is not needed and provides no benefit. (See the + /// documentation on [`OpenFlags::SQLITE_OPEN_FULL_MUTEX`] for some + /// additional discussion about this). + /// + /// Most of these are also the default settings for the C API, although + /// technically the default locking behavior is controlled by the flags used + /// when compiling SQLite -- rather than let it vary, we choose `NO_MUTEX` + /// because it's a fairly clearly the best choice for users of this library. + /// + /// # Failure + /// + /// Will return `Err` if `path` cannot be converted to a C-compatible string + /// or if the underlying SQLite open call fails. + #[inline] + pub fn open<P: AsRef<Path>>(path: P) -> Result<Connection> { + let flags = OpenFlags::default(); + Connection::open_with_flags(path, flags) + } + + /// Open a new connection to an in-memory SQLite database. + /// + /// # Failure + /// + /// Will return `Err` if the underlying SQLite open call fails. + #[inline] + pub fn open_in_memory() -> Result<Connection> { + let flags = OpenFlags::default(); + Connection::open_in_memory_with_flags(flags) + } + + /// Open a new connection to a SQLite database. + /// + /// [Database Connection](http://www.sqlite.org/c3ref/open.html) for a description of valid + /// flag combinations. + /// + /// # Failure + /// + /// Will return `Err` if `path` cannot be converted to a C-compatible + /// string or if the underlying SQLite open call fails. + #[inline] + pub fn open_with_flags<P: AsRef<Path>>(path: P, flags: OpenFlags) -> Result<Connection> { + let c_path = path_to_cstring(path.as_ref())?; + InnerConnection::open_with_flags(&c_path, flags, None).map(|db| Connection { + db: RefCell::new(db), + cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY), + path: Some(path.as_ref().to_path_buf()), + }) + } + + /// Open a new connection to a SQLite database using the specific flags and + /// vfs name. + /// + /// [Database Connection](http://www.sqlite.org/c3ref/open.html) for a description of valid + /// flag combinations. + /// + /// # Failure + /// + /// Will return `Err` if either `path` or `vfs` cannot be converted to a + /// C-compatible string or if the underlying SQLite open call fails. + #[inline] + pub fn open_with_flags_and_vfs<P: AsRef<Path>>( + path: P, + flags: OpenFlags, + vfs: &str, + ) -> Result<Connection> { + let c_path = path_to_cstring(path.as_ref())?; + let c_vfs = str_to_cstring(vfs)?; + InnerConnection::open_with_flags(&c_path, flags, Some(&c_vfs)).map(|db| Connection { + db: RefCell::new(db), + cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY), + path: Some(path.as_ref().to_path_buf()), + }) + } + + /// Open a new connection to an in-memory SQLite database. + /// + /// [Database Connection](http://www.sqlite.org/c3ref/open.html) for a description of valid + /// flag combinations. + /// + /// # Failure + /// + /// Will return `Err` if the underlying SQLite open call fails. + #[inline] + pub fn open_in_memory_with_flags(flags: OpenFlags) -> Result<Connection> { + Connection::open_with_flags(":memory:", flags) + } + + /// Open a new connection to an in-memory SQLite database using the specific + /// flags and vfs name. + /// + /// [Database Connection](http://www.sqlite.org/c3ref/open.html) for a description of valid + /// flag combinations. + /// + /// # Failure + /// + /// Will return `Err` if `vfs` cannot be converted to a C-compatible + /// string or if the underlying SQLite open call fails. + #[inline] + pub fn open_in_memory_with_flags_and_vfs(flags: OpenFlags, vfs: &str) -> Result<Connection> { + Connection::open_with_flags_and_vfs(":memory:", flags, vfs) + } + + /// Convenience method to run multiple SQL statements (that cannot take any + /// parameters). + /// + /// ## Example + /// + /// ```rust,no_run + /// # use rusqlite::{Connection, Result}; + /// fn create_tables(conn: &Connection) -> Result<()> { + /// conn.execute_batch( + /// "BEGIN; + /// CREATE TABLE foo(x INTEGER); + /// CREATE TABLE bar(y TEXT); + /// COMMIT;", + /// ) + /// } + /// ``` + /// + /// # Failure + /// + /// Will return `Err` if `sql` cannot be converted to a C-compatible string + /// or if the underlying SQLite call fails. + pub fn execute_batch(&self, sql: &str) -> Result<()> { + let mut sql = sql; + while !sql.is_empty() { + let stmt = self.prepare(sql)?; + if !stmt.stmt.is_null() && stmt.step()? && cfg!(feature = "extra_check") { + // Some PRAGMA may return rows + return Err(Error::ExecuteReturnedResults); + } + let tail = stmt.stmt.tail(); + if tail == 0 || tail >= sql.len() { + break; + } + sql = &sql[tail..]; + } + Ok(()) + } + + /// Convenience method to prepare and execute a single SQL statement. + /// + /// On success, returns the number of rows that were changed or inserted or + /// deleted (via `sqlite3_changes`). + /// + /// ## Example + /// + /// ### With positional params + /// + /// ```rust,no_run + /// # use rusqlite::{Connection}; + /// fn update_rows(conn: &Connection) { + /// match conn.execute("UPDATE foo SET bar = 'baz' WHERE qux = ?", [1i32]) { + /// Ok(updated) => println!("{} rows were updated", updated), + /// Err(err) => println!("update failed: {}", err), + /// } + /// } + /// ``` + /// + /// ### With positional params of varying types + /// + /// ```rust,no_run + /// # use rusqlite::{params, Connection}; + /// fn update_rows(conn: &Connection) { + /// match conn.execute( + /// "UPDATE foo SET bar = 'baz' WHERE qux = ?1 AND quux = ?2", + /// params![1i32, 1.5f64], + /// ) { + /// Ok(updated) => println!("{} rows were updated", updated), + /// Err(err) => println!("update failed: {}", err), + /// } + /// } + /// ``` + /// + /// ### With named params + /// + /// ```rust,no_run + /// # use rusqlite::{Connection, Result}; + /// fn insert(conn: &Connection) -> Result<usize> { + /// conn.execute( + /// "INSERT INTO test (name) VALUES (:name)", + /// &[(":name", "one")], + /// ) + /// } + /// ``` + /// + /// # Failure + /// + /// Will return `Err` if `sql` cannot be converted to a C-compatible string + /// or if the underlying SQLite call fails. + #[inline] + pub fn execute<P: Params>(&self, sql: &str, params: P) -> Result<usize> { + self.prepare(sql) + .and_then(|mut stmt| stmt.check_no_tail().and_then(|_| stmt.execute(params))) + } + + /// Returns the path to the database file, if one exists and is known. + /// + /// Note that in some cases [PRAGMA + /// database_list](https://sqlite.org/pragma.html#pragma_database_list) is + /// likely to be more robust. + #[inline] + pub fn path(&self) -> Option<&Path> { + self.path.as_deref() + } + + /// Attempts to free as much heap memory as possible from the database + /// connection. + /// + /// This calls [`sqlite3_db_release_memory`](https://www.sqlite.org/c3ref/db_release_memory.html). + #[inline] + #[cfg(feature = "release_memory")] + pub fn release_memory(&self) -> Result<()> { + self.db.borrow_mut().release_memory() + } + + /// 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 `sqlite3_changes`). + /// + /// # Failure + /// + /// Will return `Err` if `sql` cannot be converted to a C-compatible string + /// or if the underlying SQLite call fails. + #[deprecated = "You can use `execute` with named params now."] + pub fn execute_named(&self, sql: &str, params: &[(&str, &dyn ToSql)]) -> Result<usize> { + // This function itself is deprecated, so it's fine + #![allow(deprecated)] + self.prepare(sql).and_then(|mut stmt| { + stmt.check_no_tail() + .and_then(|_| stmt.execute_named(params)) + }) + } + + /// Get the SQLite rowid of the most recent successful INSERT. + /// + /// Uses [sqlite3_last_insert_rowid](https://www.sqlite.org/c3ref/last_insert_rowid.html) under + /// the hood. + #[inline] + pub fn last_insert_rowid(&self) -> i64 { + self.db.borrow_mut().last_insert_rowid() + } + + /// Convenience method to execute a query that is expected to return a + /// single row. + /// + /// ## Example + /// + /// ```rust,no_run + /// # use rusqlite::{Result, Connection}; + /// fn preferred_locale(conn: &Connection) -> Result<String> { + /// conn.query_row( + /// "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. + /// + /// Returns `Err(QueryReturnedNoRows)` if no results are returned. If the + /// query truly is optional, you can call `.optional()` on the result of + /// this to get a `Result<Option<T>>`. + /// + /// # Failure + /// + /// Will return `Err` if `sql` cannot be converted to a C-compatible string + /// or if the underlying SQLite call fails. + #[inline] + pub fn query_row<T, P, F>(&self, sql: &str, params: P, f: F) -> Result<T> + where + P: Params, + F: FnOnce(&Row<'_>) -> Result<T>, + { + let mut stmt = self.prepare(sql)?; + stmt.check_no_tail()?; + stmt.query_row(params, f) + } + + /// Convenience method to execute a query with named parameter(s) that is + /// expected to return a single row. + /// + /// If the query returns more than one row, all rows except the first are + /// ignored. + /// + /// Returns `Err(QueryReturnedNoRows)` if no results are returned. If the + /// query truly is optional, you can call `.optional()` on the result of + /// this to get a `Result<Option<T>>`. + /// + /// # Failure + /// + /// Will return `Err` if `sql` cannot be converted to a C-compatible string + /// or if the underlying SQLite call fails. + #[deprecated = "You can use `query_row` with named params now."] + pub fn query_row_named<T, F>(&self, sql: &str, params: &[(&str, &dyn ToSql)], f: F) -> Result<T> + where + F: FnOnce(&Row<'_>) -> Result<T>, + { + self.query_row(sql, params, f) + } + + /// Convenience method to execute a query that is expected to return a + /// single row, and execute a mapping via `f` on that returned row with + /// the possibility of failure. The `Result` type of `f` must implement + /// `std::convert::From<Error>`. + /// + /// ## Example + /// + /// ```rust,no_run + /// # use rusqlite::{Result, Connection}; + /// fn preferred_locale(conn: &Connection) -> Result<String> { + /// conn.query_row_and_then( + /// "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. + /// + /// # Failure + /// + /// Will return `Err` if `sql` cannot be converted to a C-compatible string + /// or if the underlying SQLite call fails. + #[inline] + pub fn query_row_and_then<T, E, P, F>(&self, sql: &str, params: P, f: F) -> Result<T, E> + where + P: Params, + F: FnOnce(&Row<'_>) -> Result<T, E>, + E: From<Error>, + { + let mut stmt = self.prepare(sql)?; + stmt.check_no_tail()?; + let mut rows = stmt.query(params)?; + + rows.get_expected_row().map_err(E::from).and_then(f) + } + + /// Prepare a SQL statement for execution. + /// + /// ## Example + /// + /// ```rust,no_run + /// # use rusqlite::{Connection, Result}; + /// fn insert_new_people(conn: &Connection) -> Result<()> { + /// let mut stmt = conn.prepare("INSERT INTO People (name) VALUES (?)")?; + /// stmt.execute(["Joe Smith"])?; + /// stmt.execute(["Bob Jones"])?; + /// Ok(()) + /// } + /// ``` + /// + /// # Failure + /// + /// Will return `Err` if `sql` cannot be converted to a C-compatible string + /// or if the underlying SQLite call fails. + #[inline] + pub fn prepare(&self, sql: &str) -> Result<Statement<'_>> { + self.db.borrow_mut().prepare(self, sql) + } + + /// Close the SQLite connection. + /// + /// This is functionally equivalent to the `Drop` implementation for + /// `Connection` except that on failure, it returns an error and the + /// connection itself (presumably so closing can be attempted again). + /// + /// # Failure + /// + /// Will return `Err` if the underlying SQLite call fails. + #[inline] + pub fn close(self) -> Result<(), (Connection, Error)> { + self.flush_prepared_statement_cache(); + let r = self.db.borrow_mut().close(); + r.map_err(move |err| (self, err)) + } + + /// Enable loading of SQLite extensions from both SQL queries and Rust. + /// + /// You must call [`Connection::load_extension_disable`] when you're + /// finished loading extensions (failure to call it can lead to bad things, + /// see "Safety"), so you should strongly consider using + /// [`LoadExtensionGuard`] instead of this function, automatically disables + /// extension loading when it goes out of scope. + /// + /// # Example + /// + /// ```rust,no_run + /// # use rusqlite::{Connection, Result}; + /// fn load_my_extension(conn: &Connection) -> Result<()> { + /// // Safety: We fully trust the loaded extension and execute no untrusted SQL + /// // while extension loading is enabled. + /// unsafe { + /// conn.load_extension_enable()?; + /// let r = conn.load_extension("my/trusted/extension", None); + /// conn.load_extension_disable()?; + /// r + /// } + /// } + /// ``` + /// + /// # Failure + /// + /// Will return `Err` if the underlying SQLite call fails. + /// + /// # Safety + /// + /// TLDR: Don't execute any untrusted queries between this call and + /// [`Connection::load_extension_disable`]. + /// + /// Perhaps surprisingly, this function does not only allow the use of + /// [`Connection::load_extension`] from Rust, but it also allows SQL queries + /// to perform [the same operation][loadext]. For example, in the period + /// between `load_extension_enable` and `load_extension_disable`, the + /// following operation will load and call some function in some dynamic + /// library: + /// + /// ```sql + /// SELECT load_extension('why_is_this_possible.dll', 'dubious_func'); + /// ``` + /// + /// This means that while this is enabled a carefully crafted SQL query can + /// be used to escalate a SQL injection attack into code execution. + /// + /// Safely using this function requires that you trust all SQL queries run + /// between when it is called, and when loading is disabled (by + /// [`Connection::load_extension_disable`]). + /// + /// [loadext]: https://www.sqlite.org/lang_corefunc.html#load_extension + #[cfg(feature = "load_extension")] + #[cfg_attr(docsrs, doc(cfg(feature = "load_extension")))] + #[inline] + pub unsafe fn load_extension_enable(&self) -> Result<()> { + self.db.borrow_mut().enable_load_extension(1) + } + + /// Disable loading of SQLite extensions. + /// + /// See [`Connection::load_extension_enable`] for an example. + /// + /// # Failure + /// + /// Will return `Err` if the underlying SQLite call fails. + #[cfg(feature = "load_extension")] + #[cfg_attr(docsrs, doc(cfg(feature = "load_extension")))] + #[inline] + pub fn load_extension_disable(&self) -> Result<()> { + // It's always safe to turn off extension loading. + unsafe { self.db.borrow_mut().enable_load_extension(0) } + } + + /// Load the SQLite extension at `dylib_path`. `dylib_path` is passed + /// through to `sqlite3_load_extension`, which may attempt OS-specific + /// modifications if the file cannot be loaded directly (for example + /// converting `"some/ext"` to `"some/ext.so"`, `"some\\ext.dll"`, ...). + /// + /// If `entry_point` is `None`, SQLite will attempt to find the entry point. + /// If it is not `None`, the entry point will be passed through to + /// `sqlite3_load_extension`. + /// + /// ## Example + /// + /// ```rust,no_run + /// # use rusqlite::{Connection, Result, LoadExtensionGuard}; + /// fn load_my_extension(conn: &Connection) -> Result<()> { + /// // Safety: we don't execute any SQL statements while + /// // extension loading is enabled. + /// let _guard = unsafe { LoadExtensionGuard::new(conn)? }; + /// // Safety: `my_sqlite_extension` is highly trustworthy. + /// unsafe { conn.load_extension("my_sqlite_extension", None) } + /// } + /// ``` + /// + /// # Failure + /// + /// Will return `Err` if the underlying SQLite call fails. + /// + /// # Safety + /// + /// This is equivalent to performing a `dlopen`/`LoadLibrary` on a shared + /// library, and calling a function inside, and thus requires that you trust + /// the library that you're loading. + /// + /// That is to say: to safely use this, the code in the extension must be + /// sound, trusted, correctly use the SQLite APIs, and not contain any + /// memory or thread safety errors. + #[cfg(feature = "load_extension")] + #[cfg_attr(docsrs, doc(cfg(feature = "load_extension")))] + #[inline] + pub unsafe fn load_extension<P: AsRef<Path>>( + &self, + dylib_path: P, + entry_point: Option<&str>, + ) -> Result<()> { + self.db + .borrow_mut() + .load_extension(dylib_path.as_ref(), entry_point) + } + + /// Get access to the underlying SQLite database connection handle. + /// + /// # Warning + /// + /// You should not need to use this function. If you do need to, please + /// [open an issue on the rusqlite repository](https://github.com/rusqlite/rusqlite/issues) and describe + /// your use case. + /// + /// # Safety + /// + /// This function is unsafe because it gives you raw access + /// to the SQLite connection, and what you do with it could impact the + /// safety of this `Connection`. + #[inline] + pub unsafe fn handle(&self) -> *mut ffi::sqlite3 { + self.db.borrow().db() + } + + /// Create a `Connection` from a raw handle. + /// + /// The underlying SQLite database connection handle will not be closed when + /// the returned connection is dropped/closed. + /// + /// # Safety + /// + /// This function is unsafe because improper use may impact the Connection. + #[inline] + pub unsafe fn from_handle(db: *mut ffi::sqlite3) -> Result<Connection> { + let db_path = db_filename(db); + let db = InnerConnection::new(db, false); + Ok(Connection { + db: RefCell::new(db), + cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY), + path: db_path, + }) + } + + /// Get access to a handle that can be used to interrupt long running + /// queries from another thread. + #[inline] + pub fn get_interrupt_handle(&self) -> InterruptHandle { + self.db.borrow().get_interrupt_handle() + } + + #[inline] + fn decode_result(&self, code: c_int) -> Result<()> { + self.db.borrow().decode_result(code) + } + + /// Return the number of rows modified, inserted or deleted by the most + /// recently completed INSERT, UPDATE or DELETE statement on the database + /// connection. + /// + /// See <https://www.sqlite.org/c3ref/changes.html> + #[inline] + pub fn changes(&self) -> u64 { + self.db.borrow().changes() + } + + /// Test for auto-commit mode. + /// Autocommit mode is on by default. + #[inline] + pub fn is_autocommit(&self) -> bool { + self.db.borrow().is_autocommit() + } + + /// Determine if all associated prepared statements have been reset. + #[inline] + #[cfg(feature = "modern_sqlite")] // 3.8.6 + #[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))] + pub fn is_busy(&self) -> bool { + self.db.borrow().is_busy() + } + + /// Flush caches to disk mid-transaction + #[cfg(feature = "modern_sqlite")] // 3.10.0 + #[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))] + pub fn cache_flush(&self) -> Result<()> { + self.db.borrow_mut().cache_flush() + } + + /// Determine if a database is read-only + #[cfg(feature = "modern_sqlite")] // 3.7.11 + #[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))] + pub fn is_readonly(&self, db_name: DatabaseName<'_>) -> Result<bool> { + self.db.borrow().db_readonly(db_name) + } +} + +impl fmt::Debug for Connection { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Connection") + .field("path", &self.path) + .finish() + } +} + +/// Batch iterator +/// ```rust +/// use rusqlite::{Batch, Connection, Result}; +/// +/// fn main() -> Result<()> { +/// let conn = Connection::open_in_memory()?; +/// let sql = r" +/// CREATE TABLE tbl1 (col); +/// CREATE TABLE tbl2 (col); +/// "; +/// let mut batch = Batch::new(&conn, sql); +/// while let Some(mut stmt) = batch.next()? { +/// stmt.execute([])?; +/// } +/// Ok(()) +/// } +/// ``` +#[derive(Debug)] +pub struct Batch<'conn, 'sql> { + conn: &'conn Connection, + sql: &'sql str, + tail: usize, +} + +impl<'conn, 'sql> Batch<'conn, 'sql> { + /// Constructor + pub fn new(conn: &'conn Connection, sql: &'sql str) -> Batch<'conn, 'sql> { + Batch { conn, sql, tail: 0 } + } + + /// Iterates on each batch statements. + /// + /// Returns `Ok(None)` when batch is completed. + #[allow(clippy::should_implement_trait)] // fallible iterator + pub fn next(&mut self) -> Result<Option<Statement<'conn>>> { + while self.tail < self.sql.len() { + let sql = &self.sql[self.tail..]; + let next = self.conn.prepare(sql)?; + let tail = next.stmt.tail(); + if tail == 0 { + self.tail = self.sql.len(); + } else { + self.tail += tail; + } + if next.stmt.is_null() { + continue; + } + return Ok(Some(next)); + } + Ok(None) + } +} + +impl<'conn> Iterator for Batch<'conn, '_> { + type Item = Result<Statement<'conn>>; + + fn next(&mut self) -> Option<Result<Statement<'conn>>> { + self.next().transpose() + } +} + +bitflags::bitflags! { + /// Flags for opening SQLite database connections. See + /// [sqlite3_open_v2](http://www.sqlite.org/c3ref/open.html) for details. + /// + /// The default open flags are `SQLITE_OPEN_READ_WRITE | SQLITE_OPEN_CREATE + /// | SQLITE_OPEN_URI | SQLITE_OPEN_NO_MUTEX`. See [`Connection::open`] for + /// some discussion about these flags. + #[repr(C)] + pub struct OpenFlags: ::std::os::raw::c_int { + /// The database is opened in read-only mode. + /// If the database does not already exist, an error is returned. + const SQLITE_OPEN_READ_ONLY = ffi::SQLITE_OPEN_READONLY; + /// The database is opened for reading and writing if possible, + /// or reading only if the file is write protected by the operating system. + /// In either case the database must already exist, otherwise an error is returned. + const SQLITE_OPEN_READ_WRITE = ffi::SQLITE_OPEN_READWRITE; + /// The database is created if it does not already exist + const SQLITE_OPEN_CREATE = ffi::SQLITE_OPEN_CREATE; + /// The filename can be interpreted as a URI if this flag is set. + const SQLITE_OPEN_URI = 0x0000_0040; + /// The database will be opened as an in-memory database. + const SQLITE_OPEN_MEMORY = 0x0000_0080; + /// The new database connection will not use a per-connection mutex (the + /// connection will use the "multi-thread" threading mode, in SQLite + /// parlance). + /// + /// This is used by default, as proper `Send`/`Sync` usage (in + /// particular, the fact that [`Connection`] does not implement `Sync`) + /// ensures thread-safety without the need to perform locking around all + /// calls. + const SQLITE_OPEN_NO_MUTEX = ffi::SQLITE_OPEN_NOMUTEX; + /// The new database connection will use a per-connection mutex -- the + /// "serialized" threading mode, in SQLite parlance. + /// + /// # Caveats + /// + /// This flag should probably never be used with `rusqlite`, as we + /// ensure thread-safety statically (we implement [`Send`] and not + /// [`Sync`]). That said + /// + /// Critically, even if this flag is used, the [`Connection`] is not + /// safe to use across multiple threads simultaneously. To access a + /// database from multiple threads, you should either create multiple + /// connections, one for each thread (if you have very many threads, + /// wrapping the `rusqlite::Connection` in a mutex is also reasonable). + /// + /// This is both because of the additional per-connection state stored + /// by `rusqlite` (for example, the prepared statement cache), and + /// because not all of SQLites functions are fully thread safe, even in + /// serialized/`SQLITE_OPEN_FULLMUTEX` mode. + /// + /// All that said, it's fairly harmless to enable this flag with + /// `rusqlite`, it will just slow things down while providing no + /// benefit. + const SQLITE_OPEN_FULL_MUTEX = ffi::SQLITE_OPEN_FULLMUTEX; + /// The database is opened with shared cache enabled. + /// + /// This is frequently useful for in-memory connections, but note that + /// broadly speaking it's discouraged by SQLite itself, which states + /// "Any use of shared cache is discouraged" in the official + /// [documentation](https://www.sqlite.org/c3ref/enable_shared_cache.html). + const SQLITE_OPEN_SHARED_CACHE = 0x0002_0000; + /// The database is opened shared cache disabled. + const SQLITE_OPEN_PRIVATE_CACHE = 0x0004_0000; + /// The database filename is not allowed to be a symbolic link. (3.31.0) + const SQLITE_OPEN_NOFOLLOW = 0x0100_0000; + /// Extended result codes. (3.37.0) + const SQLITE_OPEN_EXRESCODE = 0x0200_0000; + } +} + +impl Default for OpenFlags { + #[inline] + fn default() -> OpenFlags { + // Note: update the `Connection::open` and top-level `OpenFlags` docs if + // you change these. + OpenFlags::SQLITE_OPEN_READ_WRITE + | OpenFlags::SQLITE_OPEN_CREATE + | OpenFlags::SQLITE_OPEN_NO_MUTEX + | OpenFlags::SQLITE_OPEN_URI + } +} + +/// rusqlite's check for a safe SQLite threading mode requires SQLite 3.7.0 or +/// later. If you are running against a SQLite older than that, rusqlite +/// attempts to ensure safety by performing configuration and initialization of +/// SQLite itself the first time you +/// 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 multi-thread or serialized mode, call this function +/// prior to attempting to open a connection and rusqlite's initialization +/// process will by skipped. +/// +/// # Safety +/// +/// This function is unsafe because if you call it and SQLite has actually been +/// configured to run in single-thread mode, +/// you may encounter 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() { + BYPASS_SQLITE_INIT.store(true, Ordering::Relaxed); +} + +/// Allows interrupting a long-running computation. +pub struct InterruptHandle { + db_lock: Arc<Mutex<*mut ffi::sqlite3>>, +} + +unsafe impl Send for InterruptHandle {} +unsafe impl Sync for InterruptHandle {} + +impl InterruptHandle { + /// Interrupt the query currently executing on another thread. This will + /// cause that query to fail with a `SQLITE3_INTERRUPT` error. + pub fn interrupt(&self) { + let db_handle = self.db_lock.lock().unwrap(); + if !db_handle.is_null() { + unsafe { ffi::sqlite3_interrupt(*db_handle) } + } + } +} + +#[cfg(feature = "modern_sqlite")] // 3.7.10 +unsafe fn db_filename(db: *mut ffi::sqlite3) -> Option<PathBuf> { + let db_name = DatabaseName::Main.as_cstring().unwrap(); + let db_filename = ffi::sqlite3_db_filename(db, db_name.as_ptr()); + if db_filename.is_null() { + None + } else { + CStr::from_ptr(db_filename).to_str().ok().map(PathBuf::from) + } +} +#[cfg(not(feature = "modern_sqlite"))] +unsafe fn db_filename(_: *mut ffi::sqlite3) -> Option<PathBuf> { + None +} + +#[cfg(doctest)] +doc_comment::doctest!("../README.md"); + +#[cfg(test)] +mod test { + use super::*; + use crate::ffi; + use fallible_iterator::FallibleIterator; + use std::error::Error as StdError; + use std::fmt; + + // this function is never called, but is still type checked; in + // particular, calls with specific instantiations will require + // that those types are `Send`. + #[allow(dead_code, unconditional_recursion)] + fn ensure_send<T: Send>() { + ensure_send::<Connection>(); + ensure_send::<InterruptHandle>(); + } + + #[allow(dead_code, unconditional_recursion)] + fn ensure_sync<T: Sync>() { + ensure_sync::<InterruptHandle>(); + } + + fn checked_memory_handle() -> Connection { + Connection::open_in_memory().unwrap() + } + + #[test] + fn test_concurrent_transactions_busy_commit() -> Result<()> { + use std::time::Duration; + let tmp = tempfile::tempdir().unwrap(); + let path = tmp.path().join("transactions.db3"); + + Connection::open(&path)?.execute_batch( + " + BEGIN; CREATE TABLE foo(x INTEGER); + INSERT INTO foo VALUES(42); END;", + )?; + + let mut db1 = Connection::open_with_flags(&path, OpenFlags::SQLITE_OPEN_READ_WRITE)?; + let mut db2 = Connection::open_with_flags(&path, OpenFlags::SQLITE_OPEN_READ_ONLY)?; + + db1.busy_timeout(Duration::from_millis(0))?; + db2.busy_timeout(Duration::from_millis(0))?; + + { + let tx1 = db1.transaction()?; + let tx2 = db2.transaction()?; + + // SELECT first makes sqlite lock with a shared lock + tx1.query_row("SELECT x FROM foo LIMIT 1", [], |_| Ok(()))?; + tx2.query_row("SELECT x FROM foo LIMIT 1", [], |_| Ok(()))?; + + tx1.execute("INSERT INTO foo VALUES(?1)", [1])?; + let _ = tx2.execute("INSERT INTO foo VALUES(?1)", [2]); + + let _ = tx1.commit(); + let _ = tx2.commit(); + } + + let _ = db1 + .transaction() + .expect("commit should have closed transaction"); + let _ = db2 + .transaction() + .expect("commit should have closed transaction"); + Ok(()) + } + + #[test] + fn test_persistence() -> Result<()> { + let temp_dir = tempfile::tempdir().unwrap(); + let path = temp_dir.path().join("test.db3"); + + { + let db = Connection::open(&path)?; + let sql = "BEGIN; + CREATE TABLE foo(x INTEGER); + INSERT INTO foo VALUES(42); + END;"; + db.execute_batch(sql)?; + } + + let path_string = path.to_str().unwrap(); + let db = Connection::open(&path_string)?; + let the_answer: Result<i64> = db.query_row("SELECT x FROM foo", [], |r| r.get(0)); + + assert_eq!(42i64, the_answer?); + Ok(()) + } + + #[test] + fn test_open() { + assert!(Connection::open_in_memory().is_ok()); + + let db = checked_memory_handle(); + assert!(db.close().is_ok()); + } + + #[test] + fn test_open_failure() { + let filename = "no_such_file.db"; + let result = Connection::open_with_flags(filename, OpenFlags::SQLITE_OPEN_READ_ONLY); + assert!(result.is_err()); + let err = result.unwrap_err(); + if let Error::SqliteFailure(e, Some(msg)) = err { + assert_eq!(ErrorCode::CannotOpen, e.code); + assert_eq!(ffi::SQLITE_CANTOPEN, e.extended_code); + assert!( + msg.contains(filename), + "error message '{}' does not contain '{}'", + msg, + filename + ); + } else { + panic!("SqliteFailure expected"); + } + } + + #[cfg(unix)] + #[test] + fn test_invalid_unicode_file_names() -> Result<()> { + use std::ffi::OsStr; + use std::fs::File; + use std::os::unix::ffi::OsStrExt; + let temp_dir = tempfile::tempdir().unwrap(); + + let path = temp_dir.path(); + if File::create(path.join(OsStr::from_bytes(&[0xFE]))).is_err() { + // Skip test, filesystem doesn't support invalid Unicode + return Ok(()); + } + let db_path = path.join(OsStr::from_bytes(&[0xFF])); + { + let db = Connection::open(&db_path)?; + let sql = "BEGIN; + CREATE TABLE foo(x INTEGER); + INSERT INTO foo VALUES(42); + END;"; + db.execute_batch(sql)?; + } + + let db = Connection::open(&db_path)?; + let the_answer: Result<i64> = db.query_row("SELECT x FROM foo", [], |r| r.get(0)); + + assert_eq!(42i64, the_answer?); + Ok(()) + } + + #[test] + fn test_close_retry() -> Result<()> { + let db = Connection::open_in_memory()?; + + // force the DB to be busy by preparing a statement; this must be done at the + // FFI level to allow us to call .close() without dropping the prepared + // statement first. + let raw_stmt = { + use super::str_to_cstring; + use std::os::raw::c_int; + use std::ptr; + + let raw_db = db.db.borrow_mut().db; + let sql = "SELECT 1"; + let mut raw_stmt: *mut ffi::sqlite3_stmt = ptr::null_mut(); + let cstring = str_to_cstring(sql)?; + let rc = unsafe { + ffi::sqlite3_prepare_v2( + raw_db, + cstring.as_ptr(), + (sql.len() + 1) as c_int, + &mut raw_stmt, + ptr::null_mut(), + ) + }; + assert_eq!(rc, ffi::SQLITE_OK); + raw_stmt + }; + + // 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(); + + // finalize the open statement so a final close will succeed + assert_eq!(ffi::SQLITE_OK, unsafe { ffi::sqlite3_finalize(raw_stmt) }); + + db.close().unwrap(); + Ok(()) + } + + #[test] + fn test_open_with_flags() { + for bad_flags in &[ + OpenFlags::empty(), + OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_READ_WRITE, + OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_CREATE, + ] { + assert!(Connection::open_in_memory_with_flags(*bad_flags).is_err()); + } + } + + #[test] + fn test_execute_batch() -> Result<()> { + let db = Connection::open_in_memory()?; + let sql = "BEGIN; + CREATE TABLE foo(x INTEGER); + INSERT INTO foo VALUES(1); + INSERT INTO foo VALUES(2); + INSERT INTO foo VALUES(3); + INSERT INTO foo VALUES(4); + END;"; + db.execute_batch(sql)?; + + db.execute_batch("UPDATE foo SET x = 3 WHERE x < 3")?; + + assert!(db.execute_batch("INVALID SQL").is_err()); + Ok(()) + } + + #[test] + fn test_execute() -> Result<()> { + let db = Connection::open_in_memory()?; + db.execute_batch("CREATE TABLE foo(x INTEGER)")?; + + assert_eq!(1, db.execute("INSERT INTO foo(x) VALUES (?)", [1i32])?); + assert_eq!(1, db.execute("INSERT INTO foo(x) VALUES (?)", [2i32])?); + + assert_eq!( + 3i32, + db.query_row::<i32, _, _>("SELECT SUM(x) FROM foo", [], |r| r.get(0))? + ); + Ok(()) + } + + #[test] + #[cfg(feature = "extra_check")] + fn test_execute_select() { + let db = checked_memory_handle(); + let err = db.execute("SELECT 1 WHERE 1 < ?", [1i32]).unwrap_err(); + assert_eq!( + err, + Error::ExecuteReturnedResults, + "Unexpected error: {}", + err + ); + } + + #[test] + #[cfg(feature = "extra_check")] + fn test_execute_multiple() { + let db = checked_memory_handle(); + let err = db + .execute( + "CREATE TABLE foo(x INTEGER); CREATE TABLE foo(x INTEGER)", + [], + ) + .unwrap_err(); + match err { + Error::MultipleStatement => (), + _ => panic!("Unexpected error: {}", err), + } + } + + #[test] + fn test_prepare_column_names() -> Result<()> { + let db = Connection::open_in_memory()?; + db.execute_batch("CREATE TABLE foo(x INTEGER);")?; + + let stmt = db.prepare("SELECT * FROM foo")?; + assert_eq!(stmt.column_count(), 1); + assert_eq!(stmt.column_names(), vec!["x"]); + + let stmt = db.prepare("SELECT x AS a, x AS b FROM foo")?; + assert_eq!(stmt.column_count(), 2); + assert_eq!(stmt.column_names(), vec!["a", "b"]); + Ok(()) + } + + #[test] + fn test_prepare_execute() -> Result<()> { + let db = Connection::open_in_memory()?; + db.execute_batch("CREATE TABLE foo(x INTEGER);")?; + + let mut insert_stmt = db.prepare("INSERT INTO foo(x) VALUES(?)")?; + assert_eq!(insert_stmt.execute([1i32])?, 1); + assert_eq!(insert_stmt.execute([2i32])?, 1); + assert_eq!(insert_stmt.execute([3i32])?, 1); + + assert_eq!(insert_stmt.execute(["hello"])?, 1); + assert_eq!(insert_stmt.execute(["goodbye"])?, 1); + assert_eq!(insert_stmt.execute([types::Null])?, 1); + + let mut update_stmt = db.prepare("UPDATE foo SET x=? WHERE x<?")?; + assert_eq!(update_stmt.execute([3i32, 3i32])?, 2); + assert_eq!(update_stmt.execute([3i32, 3i32])?, 0); + assert_eq!(update_stmt.execute([8i32, 8i32])?, 3); + Ok(()) + } + + #[test] + fn test_prepare_query() -> Result<()> { + let db = Connection::open_in_memory()?; + db.execute_batch("CREATE TABLE foo(x INTEGER);")?; + + let mut insert_stmt = db.prepare("INSERT INTO foo(x) VALUES(?)")?; + assert_eq!(insert_stmt.execute([1i32])?, 1); + assert_eq!(insert_stmt.execute([2i32])?, 1); + assert_eq!(insert_stmt.execute([3i32])?, 1); + + let mut query = db.prepare("SELECT x FROM foo WHERE x < ? ORDER BY x DESC")?; + { + let mut rows = query.query([4i32])?; + let mut v = Vec::<i32>::new(); + + while let Some(row) = rows.next()? { + v.push(row.get(0)?); + } + + assert_eq!(v, [3i32, 2, 1]); + } + + { + let mut rows = query.query([3i32])?; + let mut v = Vec::<i32>::new(); + + while let Some(row) = rows.next()? { + v.push(row.get(0)?); + } + + assert_eq!(v, [2i32, 1]); + } + Ok(()) + } + + #[test] + fn test_query_map() -> Result<()> { + let db = Connection::open_in_memory()?; + let sql = "BEGIN; + CREATE TABLE foo(x INTEGER, y TEXT); + INSERT INTO foo VALUES(4, \"hello\"); + INSERT INTO foo VALUES(3, \", \"); + INSERT INTO foo VALUES(2, \"world\"); + INSERT INTO foo VALUES(1, \"!\"); + END;"; + db.execute_batch(sql)?; + + let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC")?; + let results: Result<Vec<String>> = query.query([])?.map(|row| row.get(1)).collect(); + + assert_eq!(results?.concat(), "hello, world!"); + Ok(()) + } + + #[test] + fn test_query_row() -> Result<()> { + let db = Connection::open_in_memory()?; + let sql = "BEGIN; + CREATE TABLE foo(x INTEGER); + INSERT INTO foo VALUES(1); + INSERT INTO foo VALUES(2); + INSERT INTO foo VALUES(3); + INSERT INTO foo VALUES(4); + END;"; + db.execute_batch(sql)?; + + assert_eq!( + 10i64, + db.query_row::<i64, _, _>("SELECT SUM(x) FROM foo", [], |r| r.get(0))? + ); + + let result: Result<i64> = db.query_row("SELECT x FROM foo WHERE x > 5", [], |r| r.get(0)); + match result.unwrap_err() { + Error::QueryReturnedNoRows => (), + err => panic!("Unexpected error {}", err), + } + + let bad_query_result = db.query_row("NOT A PROPER QUERY; test123", [], |_| Ok(())); + + assert!(bad_query_result.is_err()); + Ok(()) + } + + #[test] + fn test_optional() -> Result<()> { + let db = Connection::open_in_memory()?; + + let result: Result<i64> = db.query_row("SELECT 1 WHERE 0 <> 0", [], |r| r.get(0)); + let result = result.optional(); + match result? { + None => (), + _ => panic!("Unexpected result"), + } + + let result: Result<i64> = db.query_row("SELECT 1 WHERE 0 == 0", [], |r| r.get(0)); + let result = result.optional(); + match result? { + Some(1) => (), + _ => panic!("Unexpected result"), + } + + let bad_query_result: Result<i64> = db.query_row("NOT A PROPER QUERY", [], |r| r.get(0)); + let bad_query_result = bad_query_result.optional(); + assert!(bad_query_result.is_err()); + Ok(()) + } + + #[test] + fn test_pragma_query_row() -> Result<()> { + let db = Connection::open_in_memory()?; + assert_eq!( + "memory", + db.query_row::<String, _, _>("PRAGMA journal_mode", [], |r| r.get(0))? + ); + let mode = db.query_row::<String, _, _>("PRAGMA journal_mode=off", [], |r| r.get(0))?; + if cfg!(features = "bundled") { + assert_eq!(mode, "off"); + } else { + // Note: system SQLite on macOS defaults to "off" rather than + // "memory" for the journal mode (which cannot be changed for + // in-memory connections). This seems like it's *probably* legal + // according to the docs below, so we relax this test when not + // bundling: + // + // From https://www.sqlite.org/pragma.html#pragma_journal_mode + // > Note that the journal_mode for an in-memory database is either + // > MEMORY or OFF and can not be changed to a different value. An + // > attempt to change the journal_mode of an in-memory database to + // > any setting other than MEMORY or OFF is ignored. + assert!(mode == "memory" || mode == "off", "Got mode {:?}", mode); + } + + Ok(()) + } + + #[test] + fn test_prepare_failures() -> Result<()> { + let db = Connection::open_in_memory()?; + db.execute_batch("CREATE TABLE foo(x INTEGER);")?; + + let err = db.prepare("SELECT * FROM does_not_exist").unwrap_err(); + assert!(format!("{}", err).contains("does_not_exist")); + Ok(()) + } + + #[test] + fn test_last_insert_rowid() -> Result<()> { + let db = Connection::open_in_memory()?; + db.execute_batch("CREATE TABLE foo(x INTEGER PRIMARY KEY)")?; + db.execute_batch("INSERT INTO foo DEFAULT VALUES")?; + + assert_eq!(db.last_insert_rowid(), 1); + + let mut stmt = db.prepare("INSERT INTO foo DEFAULT VALUES")?; + for _ in 0i32..9 { + stmt.execute([])?; + } + assert_eq!(db.last_insert_rowid(), 10); + Ok(()) + } + + #[test] + fn test_is_autocommit() -> Result<()> { + let db = Connection::open_in_memory()?; + assert!( + db.is_autocommit(), + "autocommit expected to be active by default" + ); + Ok(()) + } + + #[test] + #[cfg(feature = "modern_sqlite")] + fn test_is_busy() -> Result<()> { + let db = Connection::open_in_memory()?; + assert!(!db.is_busy()); + let mut stmt = db.prepare("PRAGMA schema_version")?; + assert!(!db.is_busy()); + { + let mut rows = stmt.query([])?; + assert!(!db.is_busy()); + let row = rows.next()?; + assert!(db.is_busy()); + assert!(row.is_some()); + } + assert!(!db.is_busy()); + Ok(()) + } + + #[test] + fn test_statement_debugging() -> Result<()> { + let db = Connection::open_in_memory()?; + let query = "SELECT 12345"; + let stmt = db.prepare(query)?; + + assert!(format!("{:?}", stmt).contains(query)); + Ok(()) + } + + #[test] + fn test_notnull_constraint_error() -> Result<()> { + // extended error codes for constraints were added in SQLite 3.7.16; if we're + // running on our bundled version, we know the extended error code exists. + #[cfg(feature = "modern_sqlite")] + fn check_extended_code(extended_code: c_int) { + assert_eq!(extended_code, ffi::SQLITE_CONSTRAINT_NOTNULL); + } + #[cfg(not(feature = "modern_sqlite"))] + fn check_extended_code(_extended_code: c_int) {} + + let db = Connection::open_in_memory()?; + db.execute_batch("CREATE TABLE foo(x NOT NULL)")?; + + let result = db.execute("INSERT INTO foo (x) VALUES (NULL)", []); + assert!(result.is_err()); + + match result.unwrap_err() { + Error::SqliteFailure(err, _) => { + assert_eq!(err.code, ErrorCode::ConstraintViolation); + check_extended_code(err.extended_code); + } + err => panic!("Unexpected error {}", err), + } + Ok(()) + } + + #[test] + fn test_version_string() { + let n = version_number(); + let major = n / 1_000_000; + let minor = (n % 1_000_000) / 1_000; + let patch = n % 1_000; + + assert!(version().contains(&format!("{}.{}.{}", major, minor, patch))); + } + + #[test] + #[cfg(feature = "functions")] + fn test_interrupt() -> Result<()> { + let db = Connection::open_in_memory()?; + + let interrupt_handle = db.get_interrupt_handle(); + + db.create_scalar_function( + "interrupt", + 0, + functions::FunctionFlags::default(), + move |_| { + interrupt_handle.interrupt(); + Ok(0) + }, + )?; + + let mut stmt = + db.prepare("SELECT interrupt() FROM (SELECT 1 UNION SELECT 2 UNION SELECT 3)")?; + + let result: Result<Vec<i32>> = stmt.query([])?.map(|r| r.get(0)).collect(); + + assert_eq!( + result.unwrap_err().sqlite_error_code(), + Some(ErrorCode::OperationInterrupted) + ); + Ok(()) + } + + #[test] + fn test_interrupt_close() { + let db = checked_memory_handle(); + let handle = db.get_interrupt_handle(); + handle.interrupt(); + db.close().unwrap(); + handle.interrupt(); + + // Look at it's internals to see if we cleared it out properly. + let db_guard = handle.db_lock.lock().unwrap(); + assert!(db_guard.is_null()); + // It would be nice to test that we properly handle close/interrupt + // running at the same time, but it seems impossible to do with any + // degree of reliability. + } + + #[test] + fn test_get_raw() -> Result<()> { + let db = Connection::open_in_memory()?; + db.execute_batch("CREATE TABLE foo(i, x);")?; + let vals = ["foobar", "1234", "qwerty"]; + let mut insert_stmt = db.prepare("INSERT INTO foo(i, x) VALUES(?, ?)")?; + for (i, v) in vals.iter().enumerate() { + let i_to_insert = i as i64; + assert_eq!(insert_stmt.execute(params![i_to_insert, v])?, 1); + } + + let mut query = db.prepare("SELECT i, x FROM foo")?; + let mut rows = query.query([])?; + + while let Some(row) = rows.next()? { + let i = row.get_ref(0)?.as_i64()?; + let expect = vals[i as usize]; + let x = row.get_ref("x")?.as_str()?; + assert_eq!(x, expect); + } + + let mut query = db.prepare("SELECT x FROM foo")?; + let rows = query.query_map([], |row| { + let x = row.get_ref(0)?.as_str()?; // check From<FromSqlError> for Error + Ok(x[..].to_owned()) + })?; + + for (i, row) in rows.enumerate() { + assert_eq!(row?, vals[i]); + } + Ok(()) + } + + #[test] + fn test_from_handle() -> Result<()> { + let db = Connection::open_in_memory()?; + let handle = unsafe { db.handle() }; + { + let db = unsafe { Connection::from_handle(handle) }?; + db.execute_batch("PRAGMA VACUUM")?; + } + db.close().unwrap(); + Ok(()) + } + + mod query_and_then_tests { + + use super::*; + + #[derive(Debug)] + enum CustomError { + SomeError, + Sqlite(Error), + } + + impl fmt::Display for CustomError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + match *self { + CustomError::SomeError => write!(f, "my custom error"), + CustomError::Sqlite(ref se) => write!(f, "my custom error: {}", se), + } + } + } + + impl StdError for CustomError { + fn description(&self) -> &str { + "my custom error" + } + + fn cause(&self) -> Option<&dyn StdError> { + match *self { + CustomError::SomeError => None, + CustomError::Sqlite(ref se) => Some(se), + } + } + } + + impl From<Error> for CustomError { + fn from(se: Error) -> CustomError { + CustomError::Sqlite(se) + } + } + + type CustomResult<T> = Result<T, CustomError>; + + #[test] + fn test_query_and_then() -> Result<()> { + let db = Connection::open_in_memory()?; + let sql = "BEGIN; + CREATE TABLE foo(x INTEGER, y TEXT); + INSERT INTO foo VALUES(4, \"hello\"); + INSERT INTO foo VALUES(3, \", \"); + INSERT INTO foo VALUES(2, \"world\"); + INSERT INTO foo VALUES(1, \"!\"); + END;"; + db.execute_batch(sql)?; + + let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC")?; + let results: Result<Vec<String>> = + query.query_and_then([], |row| row.get(1))?.collect(); + + assert_eq!(results?.concat(), "hello, world!"); + Ok(()) + } + + #[test] + fn test_query_and_then_fails() -> Result<()> { + let db = Connection::open_in_memory()?; + let sql = "BEGIN; + CREATE TABLE foo(x INTEGER, y TEXT); + INSERT INTO foo VALUES(4, \"hello\"); + INSERT INTO foo VALUES(3, \", \"); + INSERT INTO foo VALUES(2, \"world\"); + INSERT INTO foo VALUES(1, \"!\"); + END;"; + db.execute_batch(sql)?; + + let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC")?; + let bad_type: Result<Vec<f64>> = query.query_and_then([], |row| row.get(1))?.collect(); + + match bad_type.unwrap_err() { + Error::InvalidColumnType(..) => (), + err => panic!("Unexpected error {}", err), + } + + let bad_idx: Result<Vec<String>> = + query.query_and_then([], |row| row.get(3))?.collect(); + + match bad_idx.unwrap_err() { + Error::InvalidColumnIndex(_) => (), + err => panic!("Unexpected error {}", err), + } + Ok(()) + } + + #[test] + fn test_query_and_then_custom_error() -> CustomResult<()> { + let db = Connection::open_in_memory()?; + let sql = "BEGIN; + CREATE TABLE foo(x INTEGER, y TEXT); + INSERT INTO foo VALUES(4, \"hello\"); + INSERT INTO foo VALUES(3, \", \"); + INSERT INTO foo VALUES(2, \"world\"); + INSERT INTO foo VALUES(1, \"!\"); + END;"; + db.execute_batch(sql)?; + + let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC")?; + let results: CustomResult<Vec<String>> = query + .query_and_then([], |row| row.get(1).map_err(CustomError::Sqlite))? + .collect(); + + assert_eq!(results?.concat(), "hello, world!"); + Ok(()) + } + + #[test] + fn test_query_and_then_custom_error_fails() -> Result<()> { + let db = Connection::open_in_memory()?; + let sql = "BEGIN; + CREATE TABLE foo(x INTEGER, y TEXT); + INSERT INTO foo VALUES(4, \"hello\"); + INSERT INTO foo VALUES(3, \", \"); + INSERT INTO foo VALUES(2, \"world\"); + INSERT INTO foo VALUES(1, \"!\"); + END;"; + db.execute_batch(sql)?; + + let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC")?; + let bad_type: CustomResult<Vec<f64>> = query + .query_and_then([], |row| row.get(1).map_err(CustomError::Sqlite))? + .collect(); + + match bad_type.unwrap_err() { + CustomError::Sqlite(Error::InvalidColumnType(..)) => (), + err => panic!("Unexpected error {}", err), + } + + let bad_idx: CustomResult<Vec<String>> = query + .query_and_then([], |row| row.get(3).map_err(CustomError::Sqlite))? + .collect(); + + match bad_idx.unwrap_err() { + CustomError::Sqlite(Error::InvalidColumnIndex(_)) => (), + err => panic!("Unexpected error {}", err), + } + + let non_sqlite_err: CustomResult<Vec<String>> = query + .query_and_then([], |_| Err(CustomError::SomeError))? + .collect(); + + match non_sqlite_err.unwrap_err() { + CustomError::SomeError => (), + err => panic!("Unexpected error {}", err), + } + Ok(()) + } + + #[test] + fn test_query_row_and_then_custom_error() -> CustomResult<()> { + let db = Connection::open_in_memory()?; + let sql = "BEGIN; + CREATE TABLE foo(x INTEGER, y TEXT); + INSERT INTO foo VALUES(4, \"hello\"); + END;"; + db.execute_batch(sql)?; + + let query = "SELECT x, y FROM foo ORDER BY x DESC"; + let results: CustomResult<String> = + db.query_row_and_then(query, [], |row| row.get(1).map_err(CustomError::Sqlite)); + + assert_eq!(results?, "hello"); + Ok(()) + } + + #[test] + fn test_query_row_and_then_custom_error_fails() -> Result<()> { + let db = Connection::open_in_memory()?; + let sql = "BEGIN; + CREATE TABLE foo(x INTEGER, y TEXT); + INSERT INTO foo VALUES(4, \"hello\"); + END;"; + db.execute_batch(sql)?; + + let query = "SELECT x, y FROM foo ORDER BY x DESC"; + let bad_type: CustomResult<f64> = + db.query_row_and_then(query, [], |row| row.get(1).map_err(CustomError::Sqlite)); + + match bad_type.unwrap_err() { + CustomError::Sqlite(Error::InvalidColumnType(..)) => (), + err => panic!("Unexpected error {}", err), + } + + let bad_idx: CustomResult<String> = + db.query_row_and_then(query, [], |row| row.get(3).map_err(CustomError::Sqlite)); + + match bad_idx.unwrap_err() { + CustomError::Sqlite(Error::InvalidColumnIndex(_)) => (), + err => panic!("Unexpected error {}", err), + } + + let non_sqlite_err: CustomResult<String> = + db.query_row_and_then(query, [], |_| Err(CustomError::SomeError)); + + match non_sqlite_err.unwrap_err() { + CustomError::SomeError => (), + err => panic!("Unexpected error {}", err), + } + Ok(()) + } + } + + #[test] + fn test_dynamic() -> Result<()> { + let db = Connection::open_in_memory()?; + let sql = "BEGIN; + CREATE TABLE foo(x INTEGER, y TEXT); + INSERT INTO foo VALUES(4, \"hello\"); + END;"; + db.execute_batch(sql)?; + + db.query_row("SELECT * FROM foo", [], |r| { + assert_eq!(2, r.as_ref().column_count()); + Ok(()) + }) + } + #[test] + fn test_dyn_box() -> Result<()> { + let db = Connection::open_in_memory()?; + db.execute_batch("CREATE TABLE foo(x INTEGER);")?; + let b: Box<dyn ToSql> = Box::new(5); + db.execute("INSERT INTO foo VALUES(?)", [b])?; + db.query_row("SELECT x FROM foo", [], |r| { + assert_eq!(5, r.get_unwrap::<_, i32>(0)); + Ok(()) + }) + } + + #[test] + fn test_params() -> Result<()> { + let db = Connection::open_in_memory()?; + db.query_row( + "SELECT + ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, + ?, ?, ?, ?;", + params![ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, + ], + |r| { + assert_eq!(1, r.get_unwrap::<_, i32>(0)); + Ok(()) + }, + ) + } + + #[test] + #[cfg(not(feature = "extra_check"))] + fn test_alter_table() -> Result<()> { + let db = Connection::open_in_memory()?; + db.execute_batch("CREATE TABLE x(t);")?; + // `execute_batch` should be used but `execute` should also work + db.execute("ALTER TABLE x RENAME TO y;", [])?; + Ok(()) + } + + #[test] + fn test_batch() -> Result<()> { + let db = Connection::open_in_memory()?; + let sql = r" + CREATE TABLE tbl1 (col); + CREATE TABLE tbl2 (col); + "; + let batch = Batch::new(&db, sql); + for stmt in batch { + let mut stmt = stmt?; + stmt.execute([])?; + } + Ok(()) + } + + #[test] + #[cfg(feature = "modern_sqlite")] + fn test_returning() -> Result<()> { + let db = Connection::open_in_memory()?; + db.execute_batch("CREATE TABLE foo(x INTEGER PRIMARY KEY)")?; + let row_id = + db.query_row::<i64, _, _>("INSERT INTO foo DEFAULT VALUES RETURNING ROWID", [], |r| { + r.get(0) + })?; + assert_eq!(row_id, 1); + Ok(()) + } + + #[test] + #[cfg(feature = "modern_sqlite")] + fn test_cache_flush() -> Result<()> { + let db = Connection::open_in_memory()?; + db.cache_flush() + } + + #[test] + #[cfg(feature = "modern_sqlite")] + pub fn db_readonly() -> Result<()> { + let db = Connection::open_in_memory()?; + assert!(!db.is_readonly(MAIN_DB)?); + Ok(()) + } +} |