2022-04-03 23:13:27 +08:00
|
|
|
//! 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.
|
2014-11-04 06:11:00 +08:00
|
|
|
//!
|
|
|
|
//! ```rust
|
2019-02-09 14:16:05 +08:00
|
|
|
//! use rusqlite::{params, Connection, Result};
|
2014-11-04 06:11:00 +08:00
|
|
|
//!
|
2015-02-04 07:59:58 +08:00
|
|
|
//! #[derive(Debug)]
|
2014-11-04 06:11:00 +08:00
|
|
|
//! struct Person {
|
|
|
|
//! id: i32,
|
|
|
|
//! name: String,
|
2018-08-17 00:29:46 +08:00
|
|
|
//! data: Option<Vec<u8>>,
|
2014-11-04 06:11:00 +08:00
|
|
|
//! }
|
|
|
|
//!
|
2019-02-09 14:16:05 +08:00
|
|
|
//! fn main() -> Result<()> {
|
|
|
|
//! let conn = Connection::open_in_memory()?;
|
2014-11-04 06:11:00 +08:00
|
|
|
//!
|
2018-08-17 00:29:46 +08:00
|
|
|
//! conn.execute(
|
|
|
|
//! "CREATE TABLE person (
|
2022-03-07 13:25:10 +08:00
|
|
|
//! id INTEGER PRIMARY KEY,
|
|
|
|
//! name TEXT NOT NULL,
|
|
|
|
//! data BLOB
|
|
|
|
//! )",
|
2022-03-07 14:55:34 +08:00
|
|
|
//! (), // empty list of parameters.
|
2019-02-09 14:16:05 +08:00
|
|
|
//! )?;
|
2014-11-04 06:11:00 +08:00
|
|
|
//! let me = Person {
|
|
|
|
//! id: 0,
|
|
|
|
//! name: "Steven".to_string(),
|
2018-08-17 00:29:46 +08:00
|
|
|
//! data: None,
|
2014-11-04 06:11:00 +08:00
|
|
|
//! };
|
2018-08-17 00:29:46 +08:00
|
|
|
//! conn.execute(
|
2020-07-11 23:59:29 +08:00
|
|
|
//! "INSERT INTO person (name, data) VALUES (?1, ?2)",
|
2022-03-07 14:55:34 +08:00
|
|
|
//! (&me.name, &me.data),
|
2019-02-09 14:16:05 +08:00
|
|
|
//! )?;
|
2014-11-04 06:11:00 +08:00
|
|
|
//!
|
2020-07-11 23:59:29 +08:00
|
|
|
//! let mut stmt = conn.prepare("SELECT id, name, data FROM person")?;
|
2020-11-22 16:37:00 +08:00
|
|
|
//! let person_iter = stmt.query_map([], |row| {
|
2019-02-22 03:48:09 +08:00
|
|
|
//! Ok(Person {
|
|
|
|
//! id: row.get(0)?,
|
|
|
|
//! name: row.get(1)?,
|
2020-07-11 23:59:29 +08:00
|
|
|
//! data: row.get(2)?,
|
2018-10-28 15:51:02 +08:00
|
|
|
//! })
|
2019-02-22 03:48:09 +08:00
|
|
|
//! })?;
|
2015-05-07 21:36:29 +08:00
|
|
|
//!
|
|
|
|
//! for person in person_iter {
|
|
|
|
//! println!("Found person {:?}", person.unwrap());
|
2014-11-04 06:11:00 +08:00
|
|
|
//! }
|
2019-02-09 14:16:05 +08:00
|
|
|
//! Ok(())
|
2014-11-04 06:11:00 +08:00
|
|
|
//! }
|
|
|
|
//! ```
|
2020-05-17 17:21:10 +08:00
|
|
|
#![warn(missing_docs)]
|
2021-06-13 15:17:35 +08:00
|
|
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
2016-02-14 23:11:59 +08:00
|
|
|
|
2019-01-27 06:30:27 +08:00
|
|
|
pub use libsqlite3_sys as ffi;
|
2014-10-20 07:56:41 +08:00
|
|
|
|
2018-08-11 18:48:21 +08:00
|
|
|
use std::cell::RefCell;
|
|
|
|
use std::default::Default;
|
|
|
|
use std::ffi::{CStr, CString};
|
2014-10-20 07:56:41 +08:00
|
|
|
use std::fmt;
|
2018-08-11 18:48:21 +08:00
|
|
|
use std::os::raw::{c_char, c_int};
|
|
|
|
|
2015-12-11 05:48:09 +08:00
|
|
|
use std::path::{Path, PathBuf};
|
2015-12-13 03:06:03 +08:00
|
|
|
use std::result;
|
2015-01-08 03:05:36 +08:00
|
|
|
use std::str;
|
2019-02-02 19:46:52 +08:00
|
|
|
use std::sync::atomic::Ordering;
|
|
|
|
use std::sync::{Arc, Mutex};
|
2017-03-09 05:57:43 +08:00
|
|
|
|
2018-10-31 03:11:35 +08:00
|
|
|
use crate::cache::StatementCache;
|
2019-02-02 19:46:52 +08:00
|
|
|
use crate::inner_connection::{InnerConnection, BYPASS_SQLITE_INIT};
|
2018-10-31 03:11:35 +08:00
|
|
|
use crate::raw_statement::RawStatement;
|
2019-01-30 07:33:57 +08:00
|
|
|
use crate::types::ValueRef;
|
2014-10-20 07:56:41 +08:00
|
|
|
|
2019-02-02 19:46:52 +08:00
|
|
|
pub use crate::cache::CachedStatement;
|
2019-03-20 03:43:40 +08:00
|
|
|
pub use crate::column::Column;
|
2018-10-31 03:11:35 +08:00
|
|
|
pub use crate::error::Error;
|
|
|
|
pub use crate::ffi::ErrorCode;
|
2015-12-11 05:48:09 +08:00
|
|
|
#[cfg(feature = "load_extension")]
|
2018-11-06 03:04:04 +08:00
|
|
|
pub use crate::load_extension_guard::LoadExtensionGuard;
|
2020-11-03 15:34:08 +08:00
|
|
|
pub use crate::params::{params_from_iter, Params, ParamsFromIter};
|
2020-06-29 09:52:38 +08:00
|
|
|
pub use crate::row::{AndThenRows, Map, MappedRows, Row, RowIndex, Rows};
|
2019-02-02 19:46:52 +08:00
|
|
|
pub use crate::statement::{Statement, StatementStatus};
|
|
|
|
pub use crate::transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior};
|
|
|
|
pub use crate::types::ToSql;
|
|
|
|
pub use crate::version::*;
|
2015-02-24 09:16:49 +08:00
|
|
|
|
2015-12-13 14:04:09 +08:00
|
|
|
mod error;
|
2019-02-02 22:17:20 +08:00
|
|
|
|
2017-02-09 04:11:15 +08:00
|
|
|
#[cfg(feature = "backup")]
|
2021-06-13 15:17:35 +08:00
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "backup")))]
|
2017-02-09 04:11:15 +08:00
|
|
|
pub mod backup;
|
|
|
|
#[cfg(feature = "blob")]
|
2021-06-13 15:17:35 +08:00
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "blob")))]
|
2017-02-09 04:11:15 +08:00
|
|
|
pub mod blob;
|
2018-08-11 18:48:21 +08:00
|
|
|
mod busy;
|
2016-05-18 01:01:55 +08:00
|
|
|
mod cache;
|
2019-06-18 01:20:53 +08:00
|
|
|
#[cfg(feature = "collation")]
|
2021-06-13 15:17:35 +08:00
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "collation")))]
|
2019-06-18 01:20:53 +08:00
|
|
|
mod collation;
|
2019-03-20 03:43:40 +08:00
|
|
|
mod column;
|
2019-02-02 22:17:20 +08:00
|
|
|
pub mod config;
|
2018-08-11 19:37:56 +08:00
|
|
|
#[cfg(any(feature = "functions", feature = "vtab"))]
|
|
|
|
mod context;
|
2018-08-11 18:48:21 +08:00
|
|
|
#[cfg(feature = "functions")]
|
2021-06-13 15:17:35 +08:00
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "functions")))]
|
2018-08-11 18:48:21 +08:00
|
|
|
pub mod functions;
|
|
|
|
#[cfg(feature = "hooks")]
|
2021-06-13 15:17:35 +08:00
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "hooks")))]
|
2021-06-13 16:46:00 +08:00
|
|
|
pub mod hooks;
|
2019-02-02 19:46:52 +08:00
|
|
|
mod inner_connection;
|
2017-02-09 04:11:15 +08:00
|
|
|
#[cfg(feature = "limits")]
|
2021-06-13 15:17:35 +08:00
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "limits")))]
|
2017-02-09 04:11:15 +08:00
|
|
|
pub mod limits;
|
2018-08-11 18:48:21 +08:00
|
|
|
#[cfg(feature = "load_extension")]
|
|
|
|
mod load_extension_guard;
|
2020-11-03 15:34:08 +08:00
|
|
|
mod params;
|
2019-02-03 16:17:37 +08:00
|
|
|
mod pragma;
|
2016-05-17 23:06:43 +08:00
|
|
|
mod raw_statement;
|
2017-03-09 05:57:43 +08:00
|
|
|
mod row;
|
2019-01-13 19:46:19 +08:00
|
|
|
#[cfg(feature = "session")]
|
2021-06-13 15:17:35 +08:00
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "session")))]
|
2019-01-13 19:46:19 +08:00
|
|
|
pub mod session;
|
2017-03-08 23:48:14 +08:00
|
|
|
mod statement;
|
2016-08-13 19:55:30 +08:00
|
|
|
#[cfg(feature = "trace")]
|
2021-06-13 15:17:35 +08:00
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "trace")))]
|
2016-08-13 19:55:30 +08:00
|
|
|
pub mod trace;
|
2018-08-11 18:48:21 +08:00
|
|
|
mod transaction;
|
|
|
|
pub mod types;
|
2022-01-05 12:23:32 +08:00
|
|
|
#[cfg(feature = "unlock_notify")]
|
2017-09-22 01:27:07 +08:00
|
|
|
mod unlock_notify;
|
2018-08-11 18:48:21 +08:00
|
|
|
mod version;
|
2018-05-01 04:09:41 +08:00
|
|
|
#[cfg(feature = "vtab")]
|
2021-06-13 15:17:35 +08:00
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "vtab")))]
|
2016-08-13 19:55:30 +08:00
|
|
|
pub mod vtab;
|
2014-10-20 07:56:41 +08:00
|
|
|
|
2020-04-14 14:47:12 +08:00
|
|
|
pub(crate) mod util;
|
2020-04-14 17:04:19 +08:00
|
|
|
pub(crate) use util::SmallCString;
|
2020-04-14 14:47:12 +08:00
|
|
|
|
2016-05-18 01:11:25 +08:00
|
|
|
// Number of cached prepared statements we'll hold on to.
|
|
|
|
const STATEMENT_CACHE_DEFAULT_CAPACITY: usize = 16;
|
2020-11-03 15:34:08 +08:00
|
|
|
/// To be used when your statement has no [parameter][sqlite-varparam].
|
|
|
|
///
|
|
|
|
/// [sqlite-varparam]: https://sqlite.org/lang_expr.html#varparam
|
|
|
|
///
|
2020-11-03 17:32:46 +08:00
|
|
|
/// This is deprecated in favor of using an empty array literal.
|
|
|
|
#[deprecated = "Use an empty array instead; `stmt.execute(NO_PARAMS)` => `stmt.execute([])`"]
|
2018-12-08 04:57:04 +08:00
|
|
|
pub const NO_PARAMS: &[&dyn ToSql] = &[];
|
2016-05-18 01:11:25 +08:00
|
|
|
|
2022-03-07 13:25:10 +08:00
|
|
|
/// A macro making it more convenient to longer lists of
|
2020-11-03 15:34:08 +08:00
|
|
|
/// parameters as a `&[&dyn ToSql]`.
|
2019-01-30 07:33:57 +08:00
|
|
|
///
|
|
|
|
/// # 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<()> {
|
2021-12-01 03:17:29 +08:00
|
|
|
/// conn.execute(
|
2022-03-07 13:25:10 +08:00
|
|
|
/// "INSERT INTO person(name, age_in_years, data) VALUES (?1, ?2, ?3)",
|
2021-12-01 03:17:29 +08:00
|
|
|
/// params![person.name, person.age_in_years, person.data],
|
|
|
|
/// )?;
|
2019-01-30 07:33:57 +08:00
|
|
|
/// Ok(())
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
#[macro_export]
|
|
|
|
macro_rules! params {
|
|
|
|
() => {
|
2020-11-03 17:34:18 +08:00
|
|
|
&[] as &[&dyn $crate::ToSql]
|
2019-01-30 07:33:57 +08:00
|
|
|
};
|
|
|
|
($($param:expr),+ $(,)?) => {
|
2020-01-19 02:04:28 +08:00
|
|
|
&[$(&$param as &dyn $crate::ToSql),+] as &[&dyn $crate::ToSql]
|
2019-01-30 07:33:57 +08:00
|
|
|
};
|
|
|
|
}
|
2016-05-18 01:11:25 +08:00
|
|
|
|
2019-01-30 07:33:57 +08:00
|
|
|
/// 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<()> {
|
2020-11-03 16:46:54 +08:00
|
|
|
/// conn.execute(
|
2019-01-30 07:33:57 +08:00
|
|
|
/// "INSERT INTO person (name, age_in_years, data)
|
|
|
|
/// VALUES (:name, :age, :data)",
|
2021-12-01 03:17:29 +08:00
|
|
|
/// named_params! {
|
2019-01-30 07:33:57 +08:00
|
|
|
/// ":name": person.name,
|
|
|
|
/// ":age": person.age_in_years,
|
|
|
|
/// ":data": person.data,
|
2021-12-01 03:17:29 +08:00
|
|
|
/// },
|
2019-01-30 07:33:57 +08:00
|
|
|
/// )?;
|
|
|
|
/// Ok(())
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
#[macro_export]
|
|
|
|
macro_rules! named_params {
|
|
|
|
() => {
|
2020-11-03 17:34:18 +08:00
|
|
|
&[] as &[(&str, &dyn $crate::ToSql)]
|
2019-01-30 07:33:57 +08:00
|
|
|
};
|
|
|
|
// 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),+ $(,)?) => {
|
2020-11-03 17:34:18 +08:00
|
|
|
&[$(($param_name, &$param_val as &dyn $crate::ToSql)),+] as &[(&str, &dyn $crate::ToSql)]
|
2019-01-30 07:33:57 +08:00
|
|
|
};
|
|
|
|
}
|
2015-12-13 03:06:03 +08:00
|
|
|
|
2014-11-04 06:11:00 +08:00
|
|
|
/// A typedef of the result returned by many methods.
|
2020-04-07 05:43:06 +08:00
|
|
|
pub type Result<T, E = Error> = result::Result<T, E>;
|
2014-10-20 07:56:41 +08:00
|
|
|
|
2018-12-16 18:15:21 +08:00
|
|
|
/// See the [method documentation](#tymethod.optional).
|
|
|
|
pub trait OptionalExtension<T> {
|
|
|
|
/// Converts a `Result<T>` into a `Result<Option<T>>`.
|
|
|
|
///
|
2018-12-20 04:58:33 +08:00
|
|
|
/// By default, Rusqlite treats 0 rows being returned from a query that is
|
|
|
|
/// expected to return 1 row as an error. This method will
|
2018-12-16 18:15:21 +08:00
|
|
|
/// 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),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-20 07:56:41 +08:00
|
|
|
unsafe fn errmsg_to_string(errmsg: *const c_char) -> String {
|
2015-02-24 08:52:48 +08:00
|
|
|
let c_slice = CStr::from_ptr(errmsg).to_bytes();
|
2016-05-07 18:08:57 +08:00
|
|
|
String::from_utf8_lossy(c_slice).into_owned()
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
|
|
|
|
2020-04-14 17:04:19 +08:00
|
|
|
fn str_to_cstring(s: &str) -> Result<SmallCString> {
|
|
|
|
Ok(SmallCString::new(s)?)
|
2015-02-24 08:52:48 +08:00
|
|
|
}
|
|
|
|
|
2019-02-27 11:38:41 +08:00
|
|
|
/// Returns `Ok((string ptr, len as c_int, SQLITE_STATIC | SQLITE_TRANSIENT))`
|
|
|
|
/// normally.
|
2020-03-26 02:20:05 +08:00
|
|
|
/// Returns error if the string is too large for sqlite.
|
2019-02-27 11:38:41 +08:00
|
|
|
/// 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).
|
2019-07-25 02:08:31 +08:00
|
|
|
fn str_for_sqlite(s: &[u8]) -> Result<(*const c_char, c_int, ffi::sqlite3_destructor_type)> {
|
2019-02-27 11:38:41 +08:00
|
|
|
let len = len_as_c_int(s.len())?;
|
2020-03-26 02:20:05 +08:00
|
|
|
let (ptr, dtor_info) = if len != 0 {
|
2022-01-06 02:50:25 +08:00
|
|
|
(s.as_ptr().cast::<c_char>(), ffi::SQLITE_TRANSIENT())
|
2019-02-27 11:38:41 +08:00
|
|
|
} else {
|
2020-03-26 02:20:05 +08:00
|
|
|
// Return a pointer guaranteed to live forever
|
2022-01-06 02:50:25 +08:00
|
|
|
("".as_ptr().cast::<c_char>(), ffi::SQLITE_STATIC())
|
2020-03-26 02:20:05 +08:00
|
|
|
};
|
|
|
|
Ok((ptr, len, dtor_info))
|
2019-02-27 11:38:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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> {
|
2022-05-29 19:33:51 +08:00
|
|
|
if len >= (c_int::MAX as usize) {
|
2019-03-10 18:12:14 +08:00
|
|
|
Err(Error::SqliteFailure(
|
|
|
|
ffi::Error::new(ffi::SQLITE_TOOBIG),
|
|
|
|
None,
|
|
|
|
))
|
2019-02-27 11:38:41 +08:00
|
|
|
} else {
|
|
|
|
Ok(len as c_int)
|
|
|
|
}
|
2015-02-24 08:52:48 +08:00
|
|
|
}
|
|
|
|
|
2020-04-09 16:35:38 +08:00
|
|
|
#[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))]
|
2015-12-13 03:06:03 +08:00
|
|
|
fn path_to_cstring(p: &Path) -> Result<CString> {
|
2018-10-31 03:11:35 +08:00
|
|
|
let s = p.to_str().ok_or_else(|| Error::InvalidPath(p.to_owned()))?;
|
2020-04-14 14:47:12 +08:00
|
|
|
Ok(CString::new(s)?)
|
2015-02-24 08:52:48 +08:00
|
|
|
}
|
|
|
|
|
2015-12-10 05:27:18 +08:00
|
|
|
/// Name for a database within a SQLite connection.
|
2020-11-04 11:10:23 +08:00
|
|
|
#[derive(Copy, Clone, Debug)]
|
2015-12-10 05:27:18 +08:00
|
|
|
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),
|
|
|
|
}
|
|
|
|
|
2020-11-04 11:10:23 +08:00
|
|
|
/// Shorthand for [`DatabaseName::Main`].
|
|
|
|
pub const MAIN_DB: DatabaseName<'static> = DatabaseName::Main;
|
|
|
|
|
|
|
|
/// Shorthand for [`DatabaseName::Temp`].
|
|
|
|
pub const TEMP_DB: DatabaseName<'static> = DatabaseName::Temp;
|
|
|
|
|
2018-08-17 00:29:46 +08:00
|
|
|
// Currently DatabaseName is only used by the backup and blob mods, so hide
|
|
|
|
// this (private) impl to avoid dead code warnings.
|
2019-02-03 18:02:38 +08:00
|
|
|
impl DatabaseName<'_> {
|
2020-11-04 11:10:23 +08:00
|
|
|
#[inline]
|
2022-05-29 19:33:51 +08:00
|
|
|
fn as_cstring(&self) -> Result<SmallCString> {
|
2018-08-11 18:48:21 +08:00
|
|
|
use self::DatabaseName::{Attached, Main, Temp};
|
2016-02-14 23:11:59 +08:00
|
|
|
match *self {
|
2015-12-11 05:48:09 +08:00
|
|
|
Main => str_to_cstring("main"),
|
|
|
|
Temp => str_to_cstring("temp"),
|
2015-12-10 05:27:18 +08:00
|
|
|
Attached(s) => str_to_cstring(s),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-04 06:11:00 +08:00
|
|
|
/// A connection to a SQLite database.
|
2015-12-13 02:50:12 +08:00
|
|
|
pub struct Connection {
|
|
|
|
db: RefCell<InnerConnection>,
|
2016-05-18 01:11:25 +08:00
|
|
|
cache: StatementCache,
|
2015-08-01 18:04:02 +08:00
|
|
|
path: Option<PathBuf>,
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
|
|
|
|
2015-12-13 02:50:12 +08:00
|
|
|
unsafe impl Send for Connection {}
|
2015-04-04 03:48:35 +08:00
|
|
|
|
2017-07-19 18:22:31 +08:00
|
|
|
impl Drop for Connection {
|
2020-11-04 11:10:23 +08:00
|
|
|
#[inline]
|
2017-07-19 18:22:31 +08:00
|
|
|
fn drop(&mut self) {
|
|
|
|
self.flush_prepared_statement_cache();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-13 02:50:12 +08:00
|
|
|
impl Connection {
|
2022-04-04 09:11:38 +08:00
|
|
|
/// Open a new connection to a SQLite database. If a database does not exist
|
|
|
|
/// at the path, one is created.
|
2015-12-02 01:05:29 +08:00
|
|
|
///
|
2019-06-25 02:05:36 +08:00
|
|
|
/// ```rust,no_run
|
|
|
|
/// # use rusqlite::{Connection, Result};
|
|
|
|
/// fn open_my_db() -> Result<()> {
|
|
|
|
/// let path = "./my_db.db3";
|
2022-04-04 09:11:38 +08:00
|
|
|
/// let db = Connection::open(path)?;
|
|
|
|
/// // Use the database somehow...
|
2019-06-25 02:05:36 +08:00
|
|
|
/// println!("{}", db.is_autocommit());
|
|
|
|
/// Ok(())
|
|
|
|
/// }
|
|
|
|
/// ```
|
2015-12-02 01:05:29 +08:00
|
|
|
///
|
2022-04-04 09:11:38 +08:00
|
|
|
/// # 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.
|
2022-04-07 15:17:07 +08:00
|
|
|
/// - Allow the filename to be interpreted as a URI (see <https://www.sqlite.org/uri.html#uri_filenames_in_sqlite>
|
|
|
|
/// for details).
|
2022-04-04 09:11:38 +08:00
|
|
|
/// - 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`
|
2022-04-04 13:10:44 +08:00
|
|
|
/// because it's a fairly clearly the best choice for users of this library.
|
2022-04-04 09:11:38 +08:00
|
|
|
///
|
2015-12-02 01:05:29 +08:00
|
|
|
/// # Failure
|
|
|
|
///
|
2022-04-04 09:11:38 +08:00
|
|
|
/// Will return `Err` if `path` cannot be converted to a C-compatible string
|
|
|
|
/// or if the underlying SQLite open call fails.
|
2020-11-04 11:10:23 +08:00
|
|
|
#[inline]
|
2015-12-13 03:06:03 +08:00
|
|
|
pub fn open<P: AsRef<Path>>(path: P) -> Result<Connection> {
|
2018-06-29 02:46:28 +08:00
|
|
|
let flags = OpenFlags::default();
|
2015-12-13 02:50:12 +08:00
|
|
|
Connection::open_with_flags(path, flags)
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
|
|
|
|
2015-02-07 09:07:23 +08:00
|
|
|
/// Open a new connection to an in-memory SQLite database.
|
2015-12-02 01:05:29 +08:00
|
|
|
///
|
|
|
|
/// # Failure
|
|
|
|
///
|
|
|
|
/// Will return `Err` if the underlying SQLite open call fails.
|
2020-11-04 11:10:23 +08:00
|
|
|
#[inline]
|
2015-12-13 03:06:03 +08:00
|
|
|
pub fn open_in_memory() -> Result<Connection> {
|
2018-06-29 02:46:28 +08:00
|
|
|
let flags = OpenFlags::default();
|
2015-12-13 02:50:12 +08:00
|
|
|
Connection::open_in_memory_with_flags(flags)
|
2015-02-07 09:07:23 +08:00
|
|
|
}
|
|
|
|
|
2014-11-04 06:11:00 +08:00
|
|
|
/// Open a new connection to a SQLite database.
|
|
|
|
///
|
2017-02-11 17:50:13 +08:00
|
|
|
/// [Database Connection](http://www.sqlite.org/c3ref/open.html) for a description of valid
|
2014-11-04 06:11:00 +08:00
|
|
|
/// flag combinations.
|
2015-12-02 01:05:29 +08:00
|
|
|
///
|
|
|
|
/// # Failure
|
|
|
|
///
|
2018-08-17 00:29:46 +08:00
|
|
|
/// Will return `Err` if `path` cannot be converted to a C-compatible
|
|
|
|
/// string or if the underlying SQLite open call fails.
|
2020-11-04 11:10:23 +08:00
|
|
|
#[inline]
|
2016-02-03 02:12:00 +08:00
|
|
|
pub fn open_with_flags<P: AsRef<Path>>(path: P, flags: OpenFlags) -> Result<Connection> {
|
2018-10-31 03:11:35 +08:00
|
|
|
let c_path = path_to_cstring(path.as_ref())?;
|
2019-06-17 17:42:54 +08:00
|
|
|
InnerConnection::open_with_flags(&c_path, flags, None).map(|db| Connection {
|
2018-08-11 18:48:21 +08:00
|
|
|
db: RefCell::new(db),
|
|
|
|
cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
|
|
|
|
path: Some(path.as_ref().to_path_buf()),
|
|
|
|
})
|
2016-02-03 02:12:00 +08:00
|
|
|
}
|
2015-02-07 09:07:23 +08:00
|
|
|
|
2020-02-09 19:08:25 +08:00
|
|
|
/// Open a new connection to a SQLite database using the specific flags and
|
|
|
|
/// vfs name.
|
2015-02-07 09:07:23 +08:00
|
|
|
///
|
2017-02-11 17:50:13 +08:00
|
|
|
/// [Database Connection](http://www.sqlite.org/c3ref/open.html) for a description of valid
|
2015-02-07 09:07:23 +08:00
|
|
|
/// flag combinations.
|
2015-12-02 01:05:29 +08:00
|
|
|
///
|
|
|
|
/// # Failure
|
|
|
|
///
|
2020-02-09 19:08:25 +08:00
|
|
|
/// Will return `Err` if either `path` or `vfs` cannot be converted to a
|
|
|
|
/// C-compatible string or if the underlying SQLite open call fails.
|
2020-11-04 11:10:23 +08:00
|
|
|
#[inline]
|
2020-02-09 19:08:25 +08:00
|
|
|
pub fn open_with_flags_and_vfs<P: AsRef<Path>>(
|
|
|
|
path: P,
|
|
|
|
flags: OpenFlags,
|
|
|
|
vfs: &str,
|
|
|
|
) -> Result<Connection> {
|
2019-06-17 17:42:54 +08:00
|
|
|
let c_path = path_to_cstring(path.as_ref())?;
|
|
|
|
let c_vfs = str_to_cstring(vfs)?;
|
2020-02-09 19:08:25 +08:00
|
|
|
InnerConnection::open_with_flags(&c_path, flags, Some(&c_vfs)).map(|db| Connection {
|
2018-08-11 18:48:21 +08:00
|
|
|
db: RefCell::new(db),
|
|
|
|
cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
|
|
|
|
path: Some(path.as_ref().to_path_buf()),
|
|
|
|
})
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
|
|
|
|
2015-02-07 09:07:23 +08:00
|
|
|
/// Open a new connection to an in-memory SQLite database.
|
|
|
|
///
|
2017-02-11 17:50:13 +08:00
|
|
|
/// [Database Connection](http://www.sqlite.org/c3ref/open.html) for a description of valid
|
2015-02-07 09:07:23 +08:00
|
|
|
/// flag combinations.
|
2015-12-02 01:05:29 +08:00
|
|
|
///
|
|
|
|
/// # Failure
|
|
|
|
///
|
|
|
|
/// Will return `Err` if the underlying SQLite open call fails.
|
2020-11-04 11:10:23 +08:00
|
|
|
#[inline]
|
2015-12-13 03:13:29 +08:00
|
|
|
pub fn open_in_memory_with_flags(flags: OpenFlags) -> Result<Connection> {
|
2020-02-09 19:21:20 +08:00
|
|
|
Connection::open_with_flags(":memory:", flags)
|
2019-06-17 17:42:54 +08:00
|
|
|
}
|
|
|
|
|
2020-02-09 19:08:25 +08:00
|
|
|
/// Open a new connection to an in-memory SQLite database using the specific
|
|
|
|
/// flags and vfs name.
|
2019-06-17 17:42:54 +08:00
|
|
|
///
|
|
|
|
/// [Database Connection](http://www.sqlite.org/c3ref/open.html) for a description of valid
|
|
|
|
/// flag combinations.
|
|
|
|
///
|
|
|
|
/// # Failure
|
|
|
|
///
|
2022-01-06 02:19:13 +08:00
|
|
|
/// Will return `Err` if `vfs` cannot be converted to a C-compatible
|
2019-06-17 17:42:54 +08:00
|
|
|
/// string or if the underlying SQLite open call fails.
|
2020-11-04 11:10:23 +08:00
|
|
|
#[inline]
|
2019-06-17 17:42:54 +08:00
|
|
|
pub fn open_in_memory_with_flags_and_vfs(flags: OpenFlags, vfs: &str) -> Result<Connection> {
|
2020-02-09 19:21:20 +08:00
|
|
|
Connection::open_with_flags_and_vfs(":memory:", flags, vfs)
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
|
|
|
|
2018-08-17 00:29:46 +08:00
|
|
|
/// Convenience method to run multiple SQL statements (that cannot take any
|
|
|
|
/// parameters).
|
2014-11-04 06:11:00 +08:00
|
|
|
///
|
|
|
|
/// ## Example
|
|
|
|
///
|
|
|
|
/// ```rust,no_run
|
2015-12-13 03:06:03 +08:00
|
|
|
/// # use rusqlite::{Connection, Result};
|
|
|
|
/// fn create_tables(conn: &Connection) -> Result<()> {
|
2021-12-01 03:17:29 +08:00
|
|
|
/// conn.execute_batch(
|
|
|
|
/// "BEGIN;
|
|
|
|
/// CREATE TABLE foo(x INTEGER);
|
|
|
|
/// CREATE TABLE bar(y TEXT);
|
|
|
|
/// COMMIT;",
|
2018-08-17 00:29:46 +08:00
|
|
|
/// )
|
2014-11-04 06:11:00 +08:00
|
|
|
/// }
|
|
|
|
/// ```
|
2015-12-02 01:05:29 +08:00
|
|
|
///
|
|
|
|
/// # Failure
|
|
|
|
///
|
2018-08-17 00:29:46 +08:00
|
|
|
/// Will return `Err` if `sql` cannot be converted to a C-compatible string
|
|
|
|
/// or if the underlying SQLite call fails.
|
2015-12-13 03:06:03 +08:00
|
|
|
pub fn execute_batch(&self, sql: &str) -> Result<()> {
|
2020-06-27 01:35:14 +08:00
|
|
|
let mut sql = sql;
|
|
|
|
while !sql.is_empty() {
|
|
|
|
let stmt = self.prepare(sql)?;
|
2020-08-18 01:13:55 +08:00
|
|
|
if !stmt.stmt.is_null() && stmt.step()? && cfg!(feature = "extra_check") {
|
|
|
|
// Some PRAGMA may return rows
|
2020-06-27 01:35:14 +08:00
|
|
|
return Err(Error::ExecuteReturnedResults);
|
|
|
|
}
|
|
|
|
let tail = stmt.stmt.tail();
|
|
|
|
if tail == 0 || tail >= sql.len() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
sql = &sql[tail..];
|
|
|
|
}
|
|
|
|
Ok(())
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
|
|
|
|
2014-11-04 06:11:00 +08:00
|
|
|
/// Convenience method to prepare and execute a single SQL statement.
|
|
|
|
///
|
2018-08-17 00:29:46 +08:00
|
|
|
/// On success, returns the number of rows that were changed or inserted or
|
|
|
|
/// deleted (via `sqlite3_changes`).
|
2014-11-04 06:11:00 +08:00
|
|
|
///
|
|
|
|
/// ## Example
|
|
|
|
///
|
2020-11-03 15:34:08 +08:00
|
|
|
/// ### With positional params
|
|
|
|
///
|
2014-11-04 06:11:00 +08:00
|
|
|
/// ```rust,no_run
|
2015-12-13 02:50:12 +08:00
|
|
|
/// # use rusqlite::{Connection};
|
|
|
|
/// fn update_rows(conn: &Connection) {
|
2020-11-03 15:34:08 +08:00
|
|
|
/// match conn.execute("UPDATE foo SET bar = 'baz' WHERE qux = ?", [1i32]) {
|
2014-11-04 06:11:00 +08:00
|
|
|
/// Ok(updated) => println!("{} rows were updated", updated),
|
|
|
|
/// Err(err) => println!("update failed: {}", err),
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
/// ```
|
2015-12-02 01:05:29 +08:00
|
|
|
///
|
2020-11-03 15:34:08 +08:00
|
|
|
/// ### With positional params of varying types
|
|
|
|
///
|
|
|
|
/// ```rust,no_run
|
2021-11-23 00:52:45 +08:00
|
|
|
/// # use rusqlite::{params, Connection};
|
2020-11-03 15:34:08 +08:00
|
|
|
/// fn update_rows(conn: &Connection) {
|
2021-12-01 03:17:29 +08:00
|
|
|
/// match conn.execute(
|
|
|
|
/// "UPDATE foo SET bar = 'baz' WHERE qux = ?1 AND quux = ?2",
|
|
|
|
/// params![1i32, 1.5f64],
|
|
|
|
/// ) {
|
2020-11-03 15:34:08 +08:00
|
|
|
/// 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> {
|
2020-11-03 16:46:54 +08:00
|
|
|
/// conn.execute(
|
2020-11-03 15:34:08 +08:00
|
|
|
/// "INSERT INTO test (name) VALUES (:name)",
|
2021-01-20 04:16:08 +08:00
|
|
|
/// &[(":name", "one")],
|
2020-11-03 15:34:08 +08:00
|
|
|
/// )
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
2015-12-02 01:05:29 +08:00
|
|
|
/// # Failure
|
|
|
|
///
|
2018-08-17 00:29:46 +08:00
|
|
|
/// Will return `Err` if `sql` cannot be converted to a C-compatible string
|
|
|
|
/// or if the underlying SQLite call fails.
|
2020-11-04 11:10:23 +08:00
|
|
|
#[inline]
|
2020-11-03 15:34:08 +08:00
|
|
|
pub fn execute<P: Params>(&self, sql: &str, params: P) -> Result<usize> {
|
2017-04-08 01:43:24 +08:00
|
|
|
self.prepare(sql)
|
2018-10-28 17:28:19 +08:00
|
|
|
.and_then(|mut stmt| stmt.check_no_tail().and_then(|_| stmt.execute(params)))
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
|
|
|
|
2021-05-30 13:48:48 +08:00
|
|
|
/// 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()
|
|
|
|
}
|
|
|
|
|
2022-04-17 18:00:15 +08:00
|
|
|
/// 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<()> {
|
2022-04-18 19:17:11 +08:00
|
|
|
self.db.borrow_mut().release_memory()
|
2022-04-17 18:00:15 +08:00
|
|
|
}
|
|
|
|
|
2018-08-17 00:29:46 +08:00
|
|
|
/// Convenience method to prepare and execute a single SQL statement with
|
|
|
|
/// named parameter(s).
|
2017-03-09 00:20:43 +08:00
|
|
|
///
|
2018-08-17 00:29:46 +08:00
|
|
|
/// On success, returns the number of rows that were changed or inserted or
|
|
|
|
/// deleted (via `sqlite3_changes`).
|
2017-03-09 00:20:43 +08:00
|
|
|
///
|
|
|
|
/// # Failure
|
|
|
|
///
|
2018-08-17 00:29:46 +08:00
|
|
|
/// Will return `Err` if `sql` cannot be converted to a C-compatible string
|
|
|
|
/// or if the underlying SQLite call fails.
|
2020-11-03 15:34:08 +08:00
|
|
|
#[deprecated = "You can use `execute` with named params now."]
|
2018-12-08 04:57:04 +08:00
|
|
|
pub fn execute_named(&self, sql: &str, params: &[(&str, &dyn ToSql)]) -> Result<usize> {
|
2020-11-03 15:34:08 +08:00
|
|
|
// This function itself is deprecated, so it's fine
|
|
|
|
#![allow(deprecated)]
|
2018-10-28 17:28:19 +08:00
|
|
|
self.prepare(sql).and_then(|mut stmt| {
|
|
|
|
stmt.check_no_tail()
|
|
|
|
.and_then(|_| stmt.execute_named(params))
|
|
|
|
})
|
2017-03-09 00:20:43 +08:00
|
|
|
}
|
|
|
|
|
2014-11-04 06:11:00 +08:00
|
|
|
/// 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.
|
2020-11-04 11:10:23 +08:00
|
|
|
#[inline]
|
2014-10-20 07:56:41 +08:00
|
|
|
pub fn last_insert_rowid(&self) -> i64 {
|
|
|
|
self.db.borrow_mut().last_insert_rowid()
|
|
|
|
}
|
|
|
|
|
2018-08-17 00:29:46 +08:00
|
|
|
/// Convenience method to execute a query that is expected to return a
|
|
|
|
/// single row.
|
2014-11-04 06:11:00 +08:00
|
|
|
///
|
|
|
|
/// ## Example
|
|
|
|
///
|
|
|
|
/// ```rust,no_run
|
2020-11-03 15:34:08 +08:00
|
|
|
/// # use rusqlite::{Result, Connection};
|
2015-12-13 03:06:03 +08:00
|
|
|
/// fn preferred_locale(conn: &Connection) -> Result<String> {
|
2018-08-17 00:29:46 +08:00
|
|
|
/// conn.query_row(
|
|
|
|
/// "SELECT value FROM preferences WHERE name='locale'",
|
2020-11-03 15:34:08 +08:00
|
|
|
/// [],
|
2018-08-17 00:29:46 +08:00
|
|
|
/// |row| row.get(0),
|
|
|
|
/// )
|
2014-11-04 06:11:00 +08:00
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
2018-08-17 00:29:46 +08:00
|
|
|
/// If the query returns more than one row, all rows except the first are
|
|
|
|
/// ignored.
|
2015-12-02 01:05:29 +08:00
|
|
|
///
|
2018-12-20 04:58:33 +08:00
|
|
|
/// 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>>`.
|
2015-12-02 01:05:29 +08:00
|
|
|
///
|
|
|
|
/// # Failure
|
|
|
|
///
|
2018-08-17 00:29:46 +08:00
|
|
|
/// Will return `Err` if `sql` cannot be converted to a C-compatible string
|
|
|
|
/// or if the underlying SQLite call fails.
|
2020-11-04 11:10:23 +08:00
|
|
|
#[inline]
|
2018-09-16 15:49:23 +08:00
|
|
|
pub fn query_row<T, P, F>(&self, sql: &str, params: P, f: F) -> Result<T>
|
2018-08-11 18:48:21 +08:00
|
|
|
where
|
2020-11-03 15:34:08 +08:00
|
|
|
P: Params,
|
2019-03-10 18:12:14 +08:00
|
|
|
F: FnOnce(&Row<'_>) -> Result<T>,
|
2015-12-11 09:48:38 +08:00
|
|
|
{
|
2018-10-31 03:11:35 +08:00
|
|
|
let mut stmt = self.prepare(sql)?;
|
2019-02-02 20:22:40 +08:00
|
|
|
stmt.check_no_tail()?;
|
2016-07-14 12:27:46 +08:00
|
|
|
stmt.query_row(params, f)
|
2015-12-11 09:48:38 +08:00
|
|
|
}
|
2014-10-20 07:56:41 +08:00
|
|
|
|
2022-11-11 23:20:39 +08:00
|
|
|
// https://sqlite.org/tclsqlite.html#onecolumn
|
|
|
|
#[cfg(test)]
|
|
|
|
pub(crate) fn one_column<T: crate::types::FromSql>(&self, sql: &str) -> Result<T> {
|
|
|
|
self.query_row(sql, [], |r| r.get(0))
|
|
|
|
}
|
|
|
|
|
2018-08-17 00:29:46 +08:00
|
|
|
/// Convenience method to execute a query with named parameter(s) that is
|
|
|
|
/// expected to return a single row.
|
2017-03-09 00:20:43 +08:00
|
|
|
///
|
2018-08-17 00:29:46 +08:00
|
|
|
/// If the query returns more than one row, all rows except the first are
|
|
|
|
/// ignored.
|
2017-03-09 00:20:43 +08:00
|
|
|
///
|
2018-12-20 04:58:33 +08:00
|
|
|
/// 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>>`.
|
2017-03-09 00:20:43 +08:00
|
|
|
///
|
|
|
|
/// # Failure
|
|
|
|
///
|
2018-08-17 00:29:46 +08:00
|
|
|
/// Will return `Err` if `sql` cannot be converted to a C-compatible string
|
|
|
|
/// or if the underlying SQLite call fails.
|
2020-11-03 15:34:08 +08:00
|
|
|
#[deprecated = "You can use `query_row` with named params now."]
|
2018-12-08 04:57:04 +08:00
|
|
|
pub fn query_row_named<T, F>(&self, sql: &str, params: &[(&str, &dyn ToSql)], f: F) -> Result<T>
|
2018-08-11 18:48:21 +08:00
|
|
|
where
|
2019-03-10 18:12:14 +08:00
|
|
|
F: FnOnce(&Row<'_>) -> Result<T>,
|
2017-03-09 00:20:43 +08:00
|
|
|
{
|
2020-11-03 15:34:08 +08:00
|
|
|
self.query_row(sql, params, f)
|
2017-03-09 00:20:43 +08:00
|
|
|
}
|
|
|
|
|
2018-08-17 00:29:46 +08:00
|
|
|
/// 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>`.
|
2015-08-28 02:01:01 +08:00
|
|
|
///
|
|
|
|
/// ## Example
|
|
|
|
///
|
|
|
|
/// ```rust,no_run
|
2020-11-03 15:34:08 +08:00
|
|
|
/// # use rusqlite::{Result, Connection};
|
2015-12-13 03:06:03 +08:00
|
|
|
/// fn preferred_locale(conn: &Connection) -> Result<String> {
|
2018-08-17 00:29:46 +08:00
|
|
|
/// conn.query_row_and_then(
|
|
|
|
/// "SELECT value FROM preferences WHERE name='locale'",
|
2020-11-03 15:34:08 +08:00
|
|
|
/// [],
|
2019-02-22 03:48:09 +08:00
|
|
|
/// |row| row.get(0),
|
2018-08-17 00:29:46 +08:00
|
|
|
/// )
|
2015-08-28 02:01:01 +08:00
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
2018-08-17 00:29:46 +08:00
|
|
|
/// If the query returns more than one row, all rows except the first are
|
|
|
|
/// ignored.
|
2015-12-02 01:05:29 +08:00
|
|
|
///
|
|
|
|
/// # Failure
|
|
|
|
///
|
2018-08-17 00:29:46 +08:00
|
|
|
/// Will return `Err` if `sql` cannot be converted to a C-compatible string
|
|
|
|
/// or if the underlying SQLite call fails.
|
2020-11-04 11:10:23 +08:00
|
|
|
#[inline]
|
2020-04-07 05:43:06 +08:00
|
|
|
pub fn query_row_and_then<T, E, P, F>(&self, sql: &str, params: P, f: F) -> Result<T, E>
|
2018-08-11 18:48:21 +08:00
|
|
|
where
|
2020-11-03 15:34:08 +08:00
|
|
|
P: Params,
|
2020-04-07 05:43:06 +08:00
|
|
|
F: FnOnce(&Row<'_>) -> Result<T, E>,
|
2022-05-29 19:33:51 +08:00
|
|
|
E: From<Error>,
|
2015-12-11 09:48:38 +08:00
|
|
|
{
|
2018-10-31 03:11:35 +08:00
|
|
|
let mut stmt = self.prepare(sql)?;
|
2019-02-02 20:22:40 +08:00
|
|
|
stmt.check_no_tail()?;
|
2018-10-31 03:11:35 +08:00
|
|
|
let mut rows = stmt.query(params)?;
|
2015-12-11 09:48:38 +08:00
|
|
|
|
2021-10-02 02:09:48 +08:00
|
|
|
rows.get_expected_row().map_err(E::from).and_then(f)
|
2016-02-03 02:12:00 +08:00
|
|
|
}
|
2015-01-11 11:17:49 +08:00
|
|
|
|
2014-11-04 06:11:00 +08:00
|
|
|
/// Prepare a SQL statement for execution.
|
|
|
|
///
|
|
|
|
/// ## Example
|
|
|
|
///
|
|
|
|
/// ```rust,no_run
|
2015-12-13 03:06:03 +08:00
|
|
|
/// # use rusqlite::{Connection, Result};
|
|
|
|
/// fn insert_new_people(conn: &Connection) -> Result<()> {
|
2018-10-31 03:11:35 +08:00
|
|
|
/// let mut stmt = conn.prepare("INSERT INTO People (name) VALUES (?)")?;
|
2021-01-20 04:16:08 +08:00
|
|
|
/// stmt.execute(["Joe Smith"])?;
|
|
|
|
/// stmt.execute(["Bob Jones"])?;
|
2014-11-04 06:11:00 +08:00
|
|
|
/// Ok(())
|
|
|
|
/// }
|
|
|
|
/// ```
|
2015-12-02 01:05:29 +08:00
|
|
|
///
|
|
|
|
/// # Failure
|
|
|
|
///
|
2018-08-17 00:29:46 +08:00
|
|
|
/// Will return `Err` if `sql` cannot be converted to a C-compatible string
|
|
|
|
/// or if the underlying SQLite call fails.
|
2020-11-04 11:10:23 +08:00
|
|
|
#[inline]
|
2019-02-02 18:08:04 +08:00
|
|
|
pub fn prepare(&self, sql: &str) -> Result<Statement<'_>> {
|
2014-10-20 07:56:41 +08:00
|
|
|
self.db.borrow_mut().prepare(self, sql)
|
|
|
|
}
|
|
|
|
|
2014-11-04 06:11:00 +08:00
|
|
|
/// Close the SQLite connection.
|
|
|
|
///
|
2018-08-17 00:29:46 +08:00
|
|
|
/// 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).
|
2015-12-02 01:05:29 +08:00
|
|
|
///
|
|
|
|
/// # Failure
|
|
|
|
///
|
|
|
|
/// Will return `Err` if the underlying SQLite call fails.
|
2020-11-04 11:10:23 +08:00
|
|
|
#[inline]
|
2020-04-07 05:43:06 +08:00
|
|
|
pub fn close(self) -> Result<(), (Connection, Error)> {
|
2016-11-05 03:47:28 +08:00
|
|
|
self.flush_prepared_statement_cache();
|
2017-02-09 08:38:50 +08:00
|
|
|
let r = self.db.borrow_mut().close();
|
|
|
|
r.map_err(move |err| (self, err))
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
|
|
|
|
2021-09-06 17:49:29 +08:00
|
|
|
/// Enable loading of SQLite extensions from both SQL queries and Rust.
|
2015-02-24 08:52:48 +08:00
|
|
|
///
|
2021-09-06 17:49:29 +08:00
|
|
|
/// 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
|
2015-02-24 08:52:48 +08:00
|
|
|
///
|
|
|
|
/// ```rust,no_run
|
2015-12-13 03:06:03 +08:00
|
|
|
/// # use rusqlite::{Connection, Result};
|
|
|
|
/// fn load_my_extension(conn: &Connection) -> Result<()> {
|
2021-09-06 17:49:29 +08:00
|
|
|
/// // 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
|
|
|
|
/// }
|
2015-02-24 08:52:48 +08:00
|
|
|
/// }
|
|
|
|
/// ```
|
2015-12-02 01:05:29 +08:00
|
|
|
///
|
|
|
|
/// # Failure
|
|
|
|
///
|
|
|
|
/// Will return `Err` if the underlying SQLite call fails.
|
2021-09-06 17:49:29 +08:00
|
|
|
///
|
|
|
|
/// # 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
|
2015-02-24 08:52:48 +08:00
|
|
|
#[cfg(feature = "load_extension")]
|
2021-06-13 15:17:35 +08:00
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "load_extension")))]
|
2020-11-04 11:10:23 +08:00
|
|
|
#[inline]
|
2021-09-06 17:49:29 +08:00
|
|
|
pub unsafe fn load_extension_enable(&self) -> Result<()> {
|
2015-02-24 08:52:48 +08:00
|
|
|
self.db.borrow_mut().enable_load_extension(1)
|
|
|
|
}
|
|
|
|
|
2021-06-13 15:17:35 +08:00
|
|
|
/// Disable loading of SQLite extensions.
|
2015-02-24 08:52:48 +08:00
|
|
|
///
|
2021-09-06 17:49:29 +08:00
|
|
|
/// See [`Connection::load_extension_enable`] for an example.
|
2015-12-02 01:05:29 +08:00
|
|
|
///
|
|
|
|
/// # Failure
|
|
|
|
///
|
|
|
|
/// Will return `Err` if the underlying SQLite call fails.
|
2015-02-24 08:52:48 +08:00
|
|
|
#[cfg(feature = "load_extension")]
|
2021-06-13 15:17:35 +08:00
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "load_extension")))]
|
2020-11-04 11:10:23 +08:00
|
|
|
#[inline]
|
2015-12-13 03:06:03 +08:00
|
|
|
pub fn load_extension_disable(&self) -> Result<()> {
|
2021-09-06 17:49:29 +08:00
|
|
|
// It's always safe to turn off extension loading.
|
|
|
|
unsafe { self.db.borrow_mut().enable_load_extension(0) }
|
2015-02-24 08:52:48 +08:00
|
|
|
}
|
|
|
|
|
2021-09-06 17:49:29 +08:00
|
|
|
/// 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"`, ...).
|
2015-02-24 08:52:48 +08:00
|
|
|
///
|
2021-09-06 17:49:29 +08:00
|
|
|
/// 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`.
|
2015-02-24 09:16:49 +08:00
|
|
|
///
|
|
|
|
/// ## Example
|
|
|
|
///
|
|
|
|
/// ```rust,no_run
|
2015-12-13 03:20:11 +08:00
|
|
|
/// # use rusqlite::{Connection, Result, LoadExtensionGuard};
|
2015-12-13 03:06:03 +08:00
|
|
|
/// fn load_my_extension(conn: &Connection) -> Result<()> {
|
2021-09-06 17:49:29 +08:00
|
|
|
/// // 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) }
|
2015-02-24 09:16:49 +08:00
|
|
|
/// }
|
2015-12-09 10:15:23 +08:00
|
|
|
/// ```
|
2015-12-02 01:05:29 +08:00
|
|
|
///
|
|
|
|
/// # Failure
|
|
|
|
///
|
|
|
|
/// Will return `Err` if the underlying SQLite call fails.
|
2021-09-06 17:49:29 +08:00
|
|
|
///
|
|
|
|
/// # 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.
|
2015-02-24 08:52:48 +08:00
|
|
|
#[cfg(feature = "load_extension")]
|
2021-06-13 15:17:35 +08:00
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "load_extension")))]
|
2020-11-04 11:10:23 +08:00
|
|
|
#[inline]
|
2021-09-06 17:49:29 +08:00
|
|
|
pub unsafe fn load_extension<P: AsRef<Path>>(
|
2018-08-11 18:48:21 +08:00
|
|
|
&self,
|
|
|
|
dylib_path: P,
|
|
|
|
entry_point: Option<&str>,
|
|
|
|
) -> Result<()> {
|
2017-04-08 01:43:24 +08:00
|
|
|
self.db
|
|
|
|
.borrow_mut()
|
|
|
|
.load_extension(dylib_path.as_ref(), entry_point)
|
2016-02-03 02:12:00 +08:00
|
|
|
}
|
2014-10-20 07:56:41 +08:00
|
|
|
|
2016-02-02 04:21:03 +08:00
|
|
|
/// Get access to the underlying SQLite database connection handle.
|
|
|
|
///
|
|
|
|
/// # Warning
|
|
|
|
///
|
2018-08-17 00:29:46 +08:00
|
|
|
/// You should not need to use this function. If you do need to, please
|
2020-04-16 12:07:23 +08:00
|
|
|
/// [open an issue on the rusqlite repository](https://github.com/rusqlite/rusqlite/issues) and describe
|
2019-11-03 18:19:07 +08:00
|
|
|
/// your use case.
|
|
|
|
///
|
|
|
|
/// # Safety
|
|
|
|
///
|
|
|
|
/// This function is unsafe because it gives you raw access
|
2018-08-17 00:29:46 +08:00
|
|
|
/// to the SQLite connection, and what you do with it could impact the
|
|
|
|
/// safety of this `Connection`.
|
2020-11-04 11:10:23 +08:00
|
|
|
#[inline]
|
2017-02-08 10:09:20 +08:00
|
|
|
pub unsafe fn handle(&self) -> *mut ffi::sqlite3 {
|
2016-02-02 04:21:03 +08:00
|
|
|
self.db.borrow().db()
|
|
|
|
}
|
2014-10-20 07:56:41 +08:00
|
|
|
|
2019-01-06 19:58:46 +08:00
|
|
|
/// Create a `Connection` from a raw handle.
|
|
|
|
///
|
|
|
|
/// The underlying SQLite database connection handle will not be closed when
|
|
|
|
/// the returned connection is dropped/closed.
|
2020-02-22 18:50:00 +08:00
|
|
|
///
|
|
|
|
/// # Safety
|
|
|
|
///
|
|
|
|
/// This function is unsafe because improper use may impact the Connection.
|
2020-11-04 11:10:23 +08:00
|
|
|
#[inline]
|
2019-01-06 19:58:46 +08:00
|
|
|
pub unsafe fn from_handle(db: *mut ffi::sqlite3) -> Result<Connection> {
|
2019-01-06 21:01:17 +08:00
|
|
|
let db_path = db_filename(db);
|
2019-01-06 19:58:46 +08:00
|
|
|
let db = InnerConnection::new(db, false);
|
|
|
|
Ok(Connection {
|
|
|
|
db: RefCell::new(db),
|
|
|
|
cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
|
|
|
|
path: db_path,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-10-28 15:51:02 +08:00
|
|
|
/// Get access to a handle that can be used to interrupt long running
|
|
|
|
/// queries from another thread.
|
2020-11-04 11:10:23 +08:00
|
|
|
#[inline]
|
2018-09-27 04:36:01 +08:00
|
|
|
pub fn get_interrupt_handle(&self) -> InterruptHandle {
|
|
|
|
self.db.borrow().get_interrupt_handle()
|
|
|
|
}
|
|
|
|
|
2020-11-04 11:10:23 +08:00
|
|
|
#[inline]
|
2015-12-13 03:06:03 +08:00
|
|
|
fn decode_result(&self, code: c_int) -> Result<()> {
|
2021-06-13 16:39:36 +08:00
|
|
|
self.db.borrow().decode_result(code)
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
|
|
|
|
2019-01-26 18:01:51 +08:00
|
|
|
/// Return the number of rows modified, inserted or deleted by the most
|
|
|
|
/// recently completed INSERT, UPDATE or DELETE statement on the database
|
|
|
|
/// connection.
|
2022-04-03 23:13:27 +08:00
|
|
|
///
|
|
|
|
/// See <https://www.sqlite.org/c3ref/changes.html>
|
2020-11-04 11:10:23 +08:00
|
|
|
#[inline]
|
2022-04-03 23:13:27 +08:00
|
|
|
pub fn changes(&self) -> u64 {
|
2021-06-13 16:39:36 +08:00
|
|
|
self.db.borrow().changes()
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
2018-05-13 00:35:08 +08:00
|
|
|
|
|
|
|
/// Test for auto-commit mode.
|
|
|
|
/// Autocommit mode is on by default.
|
2020-11-04 11:10:23 +08:00
|
|
|
#[inline]
|
2018-05-13 00:35:08 +08:00
|
|
|
pub fn is_autocommit(&self) -> bool {
|
|
|
|
self.db.borrow().is_autocommit()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Determine if all associated prepared statements have been reset.
|
2020-11-04 11:10:23 +08:00
|
|
|
#[inline]
|
2018-05-13 00:35:08 +08:00
|
|
|
pub fn is_busy(&self) -> bool {
|
|
|
|
self.db.borrow().is_busy()
|
|
|
|
}
|
2021-07-03 22:41:55 +08:00
|
|
|
|
|
|
|
/// Flush caches to disk mid-transaction
|
|
|
|
pub fn cache_flush(&self) -> Result<()> {
|
|
|
|
self.db.borrow_mut().cache_flush()
|
|
|
|
}
|
2022-03-18 02:58:02 +08:00
|
|
|
|
|
|
|
/// Determine if a database is read-only
|
|
|
|
pub fn is_readonly(&self, db_name: DatabaseName<'_>) -> Result<bool> {
|
|
|
|
self.db.borrow().db_readonly(db_name)
|
|
|
|
}
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
|
|
|
|
2015-12-13 02:50:12 +08:00
|
|
|
impl fmt::Debug for Connection {
|
2018-12-08 04:57:04 +08:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2015-12-13 02:50:12 +08:00
|
|
|
f.debug_struct("Connection")
|
2016-05-17 01:52:17 +08:00
|
|
|
.field("path", &self.path)
|
|
|
|
.finish()
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-29 04:10:13 +08:00
|
|
|
/// Batch iterator
|
|
|
|
/// ```rust
|
2020-11-22 16:37:00 +08:00
|
|
|
/// use rusqlite::{Batch, Connection, Result};
|
2020-10-29 04:10:13 +08:00
|
|
|
///
|
|
|
|
/// 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()? {
|
2020-11-22 16:37:00 +08:00
|
|
|
/// stmt.execute([])?;
|
2020-10-29 04:10:13 +08:00
|
|
|
/// }
|
|
|
|
/// 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 }
|
|
|
|
}
|
|
|
|
|
2020-10-31 17:47:44 +08:00
|
|
|
/// 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>>> {
|
2020-10-29 04:10:13 +08:00
|
|
|
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));
|
|
|
|
}
|
2020-10-29 04:26:36 +08:00
|
|
|
Ok(None)
|
2020-10-29 04:10:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-31 17:47:44 +08:00
|
|
|
impl<'conn> Iterator for Batch<'conn, '_> {
|
|
|
|
type Item = Result<Statement<'conn>>;
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Result<Statement<'conn>>> {
|
|
|
|
self.next().transpose()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-10 02:01:44 +08:00
|
|
|
bitflags::bitflags! {
|
2022-04-04 09:11:38 +08:00
|
|
|
/// 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.
|
2014-11-04 06:11:00 +08:00
|
|
|
#[repr(C)]
|
2017-05-29 22:52:19 +08:00
|
|
|
pub struct OpenFlags: ::std::os::raw::c_int {
|
2020-05-17 17:21:10 +08:00
|
|
|
/// The database is opened in read-only mode.
|
|
|
|
/// If the database does not already exist, an error is returned.
|
2022-04-04 09:11:38 +08:00
|
|
|
const SQLITE_OPEN_READ_ONLY = ffi::SQLITE_OPEN_READONLY;
|
2020-05-17 17:21:10 +08:00
|
|
|
/// 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.
|
2022-04-04 09:11:38 +08:00
|
|
|
const SQLITE_OPEN_READ_WRITE = ffi::SQLITE_OPEN_READWRITE;
|
2020-05-17 17:21:10 +08:00
|
|
|
/// The database is created if it does not already exist
|
2022-04-04 09:11:38 +08:00
|
|
|
const SQLITE_OPEN_CREATE = ffi::SQLITE_OPEN_CREATE;
|
2020-05-17 17:21:10 +08:00
|
|
|
/// The filename can be interpreted as a URI if this flag is set.
|
2022-09-03 02:04:18 +08:00
|
|
|
const SQLITE_OPEN_URI = ffi::SQLITE_OPEN_URI;
|
2020-05-17 17:21:10 +08:00
|
|
|
/// The database will be opened as an in-memory database.
|
2022-09-03 02:04:18 +08:00
|
|
|
const SQLITE_OPEN_MEMORY = ffi::SQLITE_OPEN_MEMORY;
|
2022-04-04 09:11:38 +08:00
|
|
|
/// 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;
|
2020-05-17 17:21:10 +08:00
|
|
|
/// The database is opened shared cache disabled.
|
2017-08-19 03:26:44 +08:00
|
|
|
const SQLITE_OPEN_PRIVATE_CACHE = 0x0004_0000;
|
2022-03-18 02:58:02 +08:00
|
|
|
/// The database filename is not allowed to be a symbolic link. (3.31.0)
|
2020-02-09 18:47:01 +08:00
|
|
|
const SQLITE_OPEN_NOFOLLOW = 0x0100_0000;
|
2022-03-18 02:58:02 +08:00
|
|
|
/// Extended result codes. (3.37.0)
|
2021-11-28 17:08:52 +08:00
|
|
|
const SQLITE_OPEN_EXRESCODE = 0x0200_0000;
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-13 03:13:29 +08:00
|
|
|
impl Default for OpenFlags {
|
2022-04-04 09:11:38 +08:00
|
|
|
#[inline]
|
2015-12-13 03:13:29 +08:00
|
|
|
fn default() -> OpenFlags {
|
2022-04-04 09:11:38 +08:00
|
|
|
// Note: update the `Connection::open` and top-level `OpenFlags` docs if
|
|
|
|
// you change these.
|
2018-08-11 18:48:21 +08:00
|
|
|
OpenFlags::SQLITE_OPEN_READ_WRITE
|
|
|
|
| OpenFlags::SQLITE_OPEN_CREATE
|
|
|
|
| OpenFlags::SQLITE_OPEN_NO_MUTEX
|
|
|
|
| OpenFlags::SQLITE_OPEN_URI
|
2015-07-07 02:24:27 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-17 00:29:46 +08:00
|
|
|
/// 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.
|
2017-02-10 09:12:24 +08:00
|
|
|
///
|
2018-08-17 00:29:46 +08:00
|
|
|
/// 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
|
2019-11-03 18:19:07 +08:00
|
|
|
/// process will by skipped.
|
|
|
|
///
|
|
|
|
/// # Safety
|
|
|
|
///
|
|
|
|
/// This function is unsafe because if you call it and SQLite has actually been
|
2018-08-17 00:29:46 +08:00
|
|
|
/// configured to run in single-thread mode,
|
2021-05-02 19:46:04 +08:00
|
|
|
/// you may encounter memory errors or data corruption or any number of terrible
|
2018-08-17 00:29:46 +08:00
|
|
|
/// things that should not be possible when you're using Rust.
|
2017-02-10 09:12:24 +08:00
|
|
|
pub unsafe fn bypass_sqlite_initialization() {
|
|
|
|
BYPASS_SQLITE_INIT.store(true, Ordering::Relaxed);
|
|
|
|
}
|
|
|
|
|
2018-09-27 04:36:01 +08:00
|
|
|
/// Allows interrupting a long-running computation.
|
|
|
|
pub struct InterruptHandle {
|
|
|
|
db_lock: Arc<Mutex<*mut ffi::sqlite3>>,
|
2017-02-10 09:12:24 +08:00
|
|
|
}
|
|
|
|
|
2018-09-27 04:36:01 +08:00
|
|
|
unsafe impl Send for InterruptHandle {}
|
|
|
|
unsafe impl Sync for InterruptHandle {}
|
2017-02-10 09:12:24 +08:00
|
|
|
|
2018-09-27 04:36:01 +08:00
|
|
|
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) }
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-06 21:01:17 +08:00
|
|
|
unsafe fn db_filename(db: *mut ffi::sqlite3) -> Option<PathBuf> {
|
2021-05-13 14:58:46 +08:00
|
|
|
let db_name = DatabaseName::Main.as_cstring().unwrap();
|
2019-01-06 21:01:17 +08:00
|
|
|
let db_filename = ffi::sqlite3_db_filename(db, db_name.as_ptr());
|
|
|
|
if db_filename.is_null() {
|
|
|
|
None
|
|
|
|
} else {
|
2020-04-09 01:38:32 +08:00
|
|
|
CStr::from_ptr(db_filename).to_str().ok().map(PathBuf::from)
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
|
|
|
}
|
2016-01-02 19:13:37 +08:00
|
|
|
|
2020-03-08 22:36:56 +08:00
|
|
|
#[cfg(doctest)]
|
|
|
|
doc_comment::doctest!("../README.md");
|
|
|
|
|
2014-10-20 07:56:41 +08:00
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
2019-08-17 14:18:37 +08:00
|
|
|
use super::*;
|
2018-10-31 03:11:35 +08:00
|
|
|
use crate::ffi;
|
2019-03-20 03:45:04 +08:00
|
|
|
use fallible_iterator::FallibleIterator;
|
2019-08-17 14:18:37 +08:00
|
|
|
use std::error::Error as StdError;
|
|
|
|
use std::fmt;
|
2014-10-20 07:56:41 +08:00
|
|
|
|
2015-03-29 18:25:46 +08:00
|
|
|
// 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>() {
|
2015-12-13 02:50:12 +08:00
|
|
|
ensure_send::<Connection>();
|
2018-09-27 04:36:01 +08:00
|
|
|
ensure_send::<InterruptHandle>();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(dead_code, unconditional_recursion)]
|
|
|
|
fn ensure_sync<T: Sync>() {
|
|
|
|
ensure_sync::<InterruptHandle>();
|
2015-03-29 18:25:46 +08:00
|
|
|
}
|
|
|
|
|
2021-07-04 22:04:13 +08:00
|
|
|
fn checked_memory_handle() -> Connection {
|
2015-12-13 02:50:12 +08:00
|
|
|
Connection::open_in_memory().unwrap()
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
|
|
|
|
2015-05-05 03:22:11 +08:00
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_concurrent_transactions_busy_commit() -> Result<()> {
|
2018-07-28 23:02:17 +08:00
|
|
|
use std::time::Duration;
|
2019-11-22 19:08:12 +08:00
|
|
|
let tmp = tempfile::tempdir().unwrap();
|
2018-07-28 19:39:19 +08:00
|
|
|
let path = tmp.path().join("transactions.db3");
|
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
Connection::open(&path)?.execute_batch(
|
|
|
|
"
|
2018-07-28 19:39:19 +08:00
|
|
|
BEGIN; CREATE TABLE foo(x INTEGER);
|
2018-08-11 18:48:21 +08:00
|
|
|
INSERT INTO foo VALUES(42); END;",
|
2020-11-06 05:14:00 +08:00
|
|
|
)?;
|
2018-07-28 19:39:19 +08:00
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
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)?;
|
2018-07-28 19:39:19 +08:00
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
db1.busy_timeout(Duration::from_millis(0))?;
|
|
|
|
db2.busy_timeout(Duration::from_millis(0))?;
|
2018-07-28 19:39:19 +08:00
|
|
|
|
|
|
|
{
|
2020-11-06 05:14:00 +08:00
|
|
|
let tx1 = db1.transaction()?;
|
|
|
|
let tx2 = db2.transaction()?;
|
2018-07-28 19:39:19 +08:00
|
|
|
|
|
|
|
// SELECT first makes sqlite lock with a shared lock
|
2020-11-06 05:14:00 +08:00
|
|
|
tx1.query_row("SELECT x FROM foo LIMIT 1", [], |_| Ok(()))?;
|
|
|
|
tx2.query_row("SELECT x FROM foo LIMIT 1", [], |_| Ok(()))?;
|
2018-07-28 19:39:19 +08:00
|
|
|
|
2021-01-20 04:16:08 +08:00
|
|
|
tx1.execute("INSERT INTO foo VALUES(?1)", [1])?;
|
2020-11-03 15:34:08 +08:00
|
|
|
let _ = tx2.execute("INSERT INTO foo VALUES(?1)", [2]);
|
2018-07-28 19:39:19 +08:00
|
|
|
|
|
|
|
let _ = tx1.commit();
|
|
|
|
let _ = tx2.commit();
|
|
|
|
}
|
|
|
|
|
2018-08-11 18:48:21 +08:00
|
|
|
let _ = db1
|
|
|
|
.transaction()
|
|
|
|
.expect("commit should have closed transaction");
|
|
|
|
let _ = db2
|
|
|
|
.transaction()
|
|
|
|
.expect("commit should have closed transaction");
|
2020-11-06 05:14:00 +08:00
|
|
|
Ok(())
|
2018-07-28 19:39:19 +08:00
|
|
|
}
|
|
|
|
|
2015-05-05 03:22:11 +08:00
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_persistence() -> Result<()> {
|
2019-11-22 19:08:12 +08:00
|
|
|
let temp_dir = tempfile::tempdir().unwrap();
|
2015-05-05 03:22:11 +08:00
|
|
|
let path = temp_dir.path().join("test.db3");
|
|
|
|
|
|
|
|
{
|
2020-11-06 05:14:00 +08:00
|
|
|
let db = Connection::open(&path)?;
|
2015-05-05 03:22:11 +08:00
|
|
|
let sql = "BEGIN;
|
|
|
|
CREATE TABLE foo(x INTEGER);
|
|
|
|
INSERT INTO foo VALUES(42);
|
|
|
|
END;";
|
2020-11-06 05:14:00 +08:00
|
|
|
db.execute_batch(sql)?;
|
2015-05-05 03:22:11 +08:00
|
|
|
}
|
2015-05-05 08:02:33 +08:00
|
|
|
|
2015-05-05 03:22:11 +08:00
|
|
|
let path_string = path.to_str().unwrap();
|
2022-10-02 17:34:58 +08:00
|
|
|
let db = Connection::open(path_string)?;
|
2022-11-11 23:32:42 +08:00
|
|
|
let the_answer: i64 = db.one_column("SELECT x FROM foo")?;
|
2015-05-05 03:22:11 +08:00
|
|
|
|
2022-11-11 23:32:42 +08:00
|
|
|
assert_eq!(42i64, the_answer);
|
2020-11-06 05:14:00 +08:00
|
|
|
Ok(())
|
2015-05-05 03:22:11 +08:00
|
|
|
}
|
|
|
|
|
2014-10-20 07:56:41 +08:00
|
|
|
#[test]
|
|
|
|
fn test_open() {
|
2022-07-31 13:00:37 +08:00
|
|
|
Connection::open_in_memory().unwrap();
|
2014-10-20 07:56:41 +08:00
|
|
|
|
|
|
|
let db = checked_memory_handle();
|
2022-07-31 13:00:37 +08:00
|
|
|
db.close().unwrap();
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
|
|
|
|
2018-03-11 02:17:22 +08:00
|
|
|
#[test]
|
|
|
|
fn test_open_failure() {
|
|
|
|
let filename = "no_such_file.db";
|
|
|
|
let result = Connection::open_with_flags(filename, OpenFlags::SQLITE_OPEN_READ_ONLY);
|
2022-04-07 15:17:42 +08:00
|
|
|
let err = result.unwrap_err();
|
2018-03-11 02:17:22 +08:00
|
|
|
if let Error::SqliteFailure(e, Some(msg)) = err {
|
|
|
|
assert_eq!(ErrorCode::CannotOpen, e.code);
|
|
|
|
assert_eq!(ffi::SQLITE_CANTOPEN, e.extended_code);
|
2019-10-30 02:24:18 +08:00
|
|
|
assert!(
|
|
|
|
msg.contains(filename),
|
|
|
|
"error message '{}' does not contain '{}'",
|
|
|
|
msg,
|
|
|
|
filename
|
|
|
|
);
|
2018-03-11 02:17:22 +08:00
|
|
|
} else {
|
|
|
|
panic!("SqliteFailure expected");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-09 16:35:38 +08:00
|
|
|
#[cfg(unix)]
|
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_invalid_unicode_file_names() -> Result<()> {
|
2020-04-09 16:35:38 +08:00
|
|
|
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
|
2020-11-06 05:14:00 +08:00
|
|
|
return Ok(());
|
2020-04-09 16:35:38 +08:00
|
|
|
}
|
|
|
|
let db_path = path.join(OsStr::from_bytes(&[0xFF]));
|
|
|
|
{
|
2020-11-06 05:14:00 +08:00
|
|
|
let db = Connection::open(&db_path)?;
|
2020-04-09 16:35:38 +08:00
|
|
|
let sql = "BEGIN;
|
|
|
|
CREATE TABLE foo(x INTEGER);
|
|
|
|
INSERT INTO foo VALUES(42);
|
|
|
|
END;";
|
2020-11-06 05:14:00 +08:00
|
|
|
db.execute_batch(sql)?;
|
2020-04-09 16:35:38 +08:00
|
|
|
}
|
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
let db = Connection::open(&db_path)?;
|
2022-11-11 23:32:42 +08:00
|
|
|
let the_answer: i64 = db.one_column("SELECT x FROM foo")?;
|
2020-04-09 16:35:38 +08:00
|
|
|
|
2022-11-11 23:32:42 +08:00
|
|
|
assert_eq!(42i64, the_answer);
|
2020-11-06 05:14:00 +08:00
|
|
|
Ok(())
|
2020-04-09 16:35:38 +08:00
|
|
|
}
|
|
|
|
|
2017-01-04 09:00:29 +08:00
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_close_retry() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2017-01-04 09:00:29 +08:00
|
|
|
|
2018-08-17 00:29:46 +08:00
|
|
|
// 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.
|
2017-01-04 09:00:29 +08:00
|
|
|
let raw_stmt = {
|
|
|
|
use super::str_to_cstring;
|
2017-02-17 00:17:24 +08:00
|
|
|
use std::os::raw::c_int;
|
2018-08-11 18:48:21 +08:00
|
|
|
use std::ptr;
|
2017-01-04 09:00:29 +08:00
|
|
|
|
|
|
|
let raw_db = db.db.borrow_mut().db;
|
|
|
|
let sql = "SELECT 1";
|
2020-04-07 10:53:55 +08:00
|
|
|
let mut raw_stmt: *mut ffi::sqlite3_stmt = ptr::null_mut();
|
2020-11-06 05:14:00 +08:00
|
|
|
let cstring = str_to_cstring(sql)?;
|
2017-01-04 09:00:29 +08:00
|
|
|
let rc = unsafe {
|
2018-08-11 18:48:21 +08:00
|
|
|
ffi::sqlite3_prepare_v2(
|
|
|
|
raw_db,
|
2019-11-03 18:19:07 +08:00
|
|
|
cstring.as_ptr(),
|
2018-08-11 18:48:21 +08:00
|
|
|
(sql.len() + 1) as c_int,
|
2020-04-07 10:53:55 +08:00
|
|
|
&mut raw_stmt,
|
2018-08-11 18:48:21 +08:00
|
|
|
ptr::null_mut(),
|
|
|
|
)
|
2017-01-04 09:00:29 +08:00
|
|
|
};
|
|
|
|
assert_eq!(rc, ffi::SQLITE_OK);
|
|
|
|
raw_stmt
|
|
|
|
};
|
|
|
|
|
2018-08-17 00:29:46 +08:00
|
|
|
// now that we have an open statement, trying (and retrying) to close should
|
|
|
|
// fail.
|
2017-01-07 03:32:27 +08:00
|
|
|
let (db, _) = db.close().unwrap_err();
|
|
|
|
let (db, _) = db.close().unwrap_err();
|
|
|
|
let (db, _) = db.close().unwrap_err();
|
2017-01-04 09:00:29 +08:00
|
|
|
|
2017-01-07 03:32:27 +08:00
|
|
|
// finalize the open statement so a final close will succeed
|
2017-01-04 09:00:29 +08:00
|
|
|
assert_eq!(ffi::SQLITE_OK, unsafe { ffi::sqlite3_finalize(raw_stmt) });
|
|
|
|
|
|
|
|
db.close().unwrap();
|
2020-11-06 05:14:00 +08:00
|
|
|
Ok(())
|
2017-01-04 09:00:29 +08:00
|
|
|
}
|
|
|
|
|
2014-10-20 07:56:41 +08:00
|
|
|
#[test]
|
|
|
|
fn test_open_with_flags() {
|
2018-08-11 18:48:21 +08:00
|
|
|
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,
|
|
|
|
] {
|
2022-07-31 13:00:37 +08:00
|
|
|
Connection::open_in_memory_with_flags(*bad_flags).unwrap_err();
|
2016-02-03 02:12:00 +08:00
|
|
|
}
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_execute_batch() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2014-10-20 07:56:41 +08:00
|
|
|
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;";
|
2020-11-06 05:14:00 +08:00
|
|
|
db.execute_batch(sql)?;
|
2014-10-20 07:56:41 +08:00
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
db.execute_batch("UPDATE foo SET x = 3 WHERE x < 3")?;
|
2014-10-20 07:56:41 +08:00
|
|
|
|
2022-07-31 13:00:37 +08:00
|
|
|
db.execute_batch("INVALID SQL").unwrap_err();
|
2020-11-06 05:14:00 +08:00
|
|
|
Ok(())
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_execute() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2020-11-06 05:14:00 +08:00
|
|
|
db.execute_batch("CREATE TABLE foo(x INTEGER)")?;
|
2014-10-20 07:56:41 +08:00
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
assert_eq!(1, db.execute("INSERT INTO foo(x) VALUES (?)", [1i32])?);
|
|
|
|
assert_eq!(1, db.execute("INSERT INTO foo(x) VALUES (?)", [2i32])?);
|
2014-10-20 07:56:41 +08:00
|
|
|
|
2022-11-11 23:20:39 +08:00
|
|
|
assert_eq!(3i32, db.one_column::<i32>("SELECT SUM(x) FROM foo")?);
|
2020-11-06 05:14:00 +08:00
|
|
|
Ok(())
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
|
|
|
|
2015-08-01 14:09:59 +08:00
|
|
|
#[test]
|
2020-03-05 03:42:32 +08:00
|
|
|
#[cfg(feature = "extra_check")]
|
2015-08-01 14:09:59 +08:00
|
|
|
fn test_execute_select() {
|
|
|
|
let db = checked_memory_handle();
|
2020-11-03 15:34:08 +08:00
|
|
|
let err = db.execute("SELECT 1 WHERE 1 < ?", [1i32]).unwrap_err();
|
2022-05-29 19:33:51 +08:00
|
|
|
assert_eq!(
|
|
|
|
err,
|
|
|
|
Error::ExecuteReturnedResults,
|
2021-10-02 02:09:48 +08:00
|
|
|
"Unexpected error: {}",
|
|
|
|
err
|
|
|
|
);
|
2015-08-01 14:09:59 +08:00
|
|
|
}
|
|
|
|
|
2018-10-28 17:28:19 +08:00
|
|
|
#[test]
|
2019-08-31 15:39:09 +08:00
|
|
|
#[cfg(feature = "extra_check")]
|
2018-10-28 17:28:19 +08:00
|
|
|
fn test_execute_multiple() {
|
|
|
|
let db = checked_memory_handle();
|
|
|
|
let err = db
|
|
|
|
.execute(
|
|
|
|
"CREATE TABLE foo(x INTEGER); CREATE TABLE foo(x INTEGER)",
|
2020-11-03 15:34:08 +08:00
|
|
|
[],
|
2019-08-27 02:43:39 +08:00
|
|
|
)
|
|
|
|
.unwrap_err();
|
2015-12-13 13:54:08 +08:00
|
|
|
match err {
|
2018-10-28 17:28:19 +08:00
|
|
|
Error::MultipleStatement => (),
|
2015-12-13 13:54:08 +08:00
|
|
|
_ => panic!("Unexpected error: {}", err),
|
|
|
|
}
|
2015-08-01 14:09:59 +08:00
|
|
|
}
|
|
|
|
|
2015-07-25 10:11:59 +08:00
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_prepare_column_names() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2020-11-06 05:14:00 +08:00
|
|
|
db.execute_batch("CREATE TABLE foo(x INTEGER);")?;
|
2015-07-25 10:11:59 +08:00
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
let stmt = db.prepare("SELECT * FROM foo")?;
|
2016-01-02 17:28:00 +08:00
|
|
|
assert_eq!(stmt.column_count(), 1);
|
2015-07-25 10:11:59 +08:00
|
|
|
assert_eq!(stmt.column_names(), vec!["x"]);
|
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
let stmt = db.prepare("SELECT x AS a, x AS b FROM foo")?;
|
2016-01-02 17:28:00 +08:00
|
|
|
assert_eq!(stmt.column_count(), 2);
|
2015-07-25 10:11:59 +08:00
|
|
|
assert_eq!(stmt.column_names(), vec!["a", "b"]);
|
2020-11-06 05:14:00 +08:00
|
|
|
Ok(())
|
2015-07-25 10:11:59 +08:00
|
|
|
}
|
|
|
|
|
2014-10-20 07:56:41 +08:00
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_prepare_execute() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2020-11-06 05:14:00 +08:00
|
|
|
db.execute_batch("CREATE TABLE foo(x INTEGER);")?;
|
2014-10-20 07:56:41 +08:00
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
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);
|
2014-10-20 07:56:41 +08:00
|
|
|
|
2021-01-20 04:16:08 +08:00
|
|
|
assert_eq!(insert_stmt.execute(["hello"])?, 1);
|
|
|
|
assert_eq!(insert_stmt.execute(["goodbye"])?, 1);
|
2020-11-06 05:14:00 +08:00
|
|
|
assert_eq!(insert_stmt.execute([types::Null])?, 1);
|
2014-10-20 07:56:41 +08:00
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
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(())
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_prepare_query() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2020-11-06 05:14:00 +08:00
|
|
|
db.execute_batch("CREATE TABLE foo(x INTEGER);")?;
|
2014-10-20 07:56:41 +08:00
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
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);
|
2014-10-20 07:56:41 +08:00
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
let mut query = db.prepare("SELECT x FROM foo WHERE x < ? ORDER BY x DESC")?;
|
2014-10-20 07:56:41 +08:00
|
|
|
{
|
2020-11-06 05:14:00 +08:00
|
|
|
let mut rows = query.query([4i32])?;
|
2016-05-19 00:33:58 +08:00
|
|
|
let mut v = Vec::<i32>::new();
|
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
while let Some(row) = rows.next()? {
|
|
|
|
v.push(row.get(0)?);
|
2016-05-19 00:33:58 +08:00
|
|
|
}
|
2014-10-20 07:56:41 +08:00
|
|
|
|
2015-03-17 12:55:28 +08:00
|
|
|
assert_eq!(v, [3i32, 2, 1]);
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2020-11-06 05:14:00 +08:00
|
|
|
let mut rows = query.query([3i32])?;
|
2016-05-19 00:33:58 +08:00
|
|
|
let mut v = Vec::<i32>::new();
|
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
while let Some(row) = rows.next()? {
|
|
|
|
v.push(row.get(0)?);
|
2016-05-19 00:33:58 +08:00
|
|
|
}
|
|
|
|
|
2015-03-17 12:55:28 +08:00
|
|
|
assert_eq!(v, [2i32, 1]);
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
2020-11-06 05:14:00 +08:00
|
|
|
Ok(())
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
|
|
|
|
2015-05-05 21:29:46 +08:00
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_query_map() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2015-05-05 21:29:46 +08:00
|
|
|
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;";
|
2020-11-06 05:14:00 +08:00
|
|
|
db.execute_batch(sql)?;
|
2015-05-05 21:29:46 +08:00
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
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();
|
2015-05-05 21:29:46 +08:00
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
assert_eq!(results?.concat(), "hello, world!");
|
|
|
|
Ok(())
|
2015-05-05 21:29:46 +08:00
|
|
|
}
|
|
|
|
|
2015-01-11 11:17:49 +08:00
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_query_row() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2015-01-11 11:17:49 +08:00
|
|
|
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;";
|
2020-11-06 05:14:00 +08:00
|
|
|
db.execute_batch(sql)?;
|
2015-01-11 11:17:49 +08:00
|
|
|
|
2022-11-11 23:20:39 +08:00
|
|
|
assert_eq!(10i64, db.one_column::<i64>("SELECT SUM(x) FROM foo")?);
|
2015-01-11 11:17:49 +08:00
|
|
|
|
2022-11-11 23:20:39 +08:00
|
|
|
let result: Result<i64> = db.one_column("SELECT x FROM foo WHERE x > 5");
|
2015-12-13 13:54:08 +08:00
|
|
|
match result.unwrap_err() {
|
|
|
|
Error::QueryReturnedNoRows => (),
|
|
|
|
err => panic!("Unexpected error {}", err),
|
|
|
|
}
|
2015-01-11 11:17:49 +08:00
|
|
|
|
2020-11-03 15:34:08 +08:00
|
|
|
let bad_query_result = db.query_row("NOT A PROPER QUERY; test123", [], |_| Ok(()));
|
2015-01-11 11:17:49 +08:00
|
|
|
|
2022-07-31 13:00:37 +08:00
|
|
|
bad_query_result.unwrap_err();
|
2020-11-06 05:14:00 +08:00
|
|
|
Ok(())
|
2015-01-11 11:17:49 +08:00
|
|
|
}
|
|
|
|
|
2018-12-16 18:15:21 +08:00
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_optional() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2018-12-16 18:15:21 +08:00
|
|
|
|
2022-11-11 23:20:39 +08:00
|
|
|
let result: Result<i64> = db.one_column("SELECT 1 WHERE 0 <> 0");
|
2018-12-16 18:15:21 +08:00
|
|
|
let result = result.optional();
|
2020-11-06 05:14:00 +08:00
|
|
|
match result? {
|
2018-12-16 18:15:21 +08:00
|
|
|
None => (),
|
|
|
|
_ => panic!("Unexpected result"),
|
|
|
|
}
|
|
|
|
|
2022-11-11 23:20:39 +08:00
|
|
|
let result: Result<i64> = db.one_column("SELECT 1 WHERE 0 == 0");
|
2018-12-16 18:15:21 +08:00
|
|
|
let result = result.optional();
|
2020-11-06 05:14:00 +08:00
|
|
|
match result? {
|
2018-12-16 18:15:21 +08:00
|
|
|
Some(1) => (),
|
|
|
|
_ => panic!("Unexpected result"),
|
|
|
|
}
|
2015-01-11 11:17:49 +08:00
|
|
|
|
2022-11-11 23:20:39 +08:00
|
|
|
let bad_query_result: Result<i64> = db.one_column("NOT A PROPER QUERY");
|
2018-12-16 18:15:21 +08:00
|
|
|
let bad_query_result = bad_query_result.optional();
|
2022-07-31 13:00:37 +08:00
|
|
|
bad_query_result.unwrap_err();
|
2020-11-06 05:14:00 +08:00
|
|
|
Ok(())
|
2015-01-11 11:17:49 +08:00
|
|
|
}
|
|
|
|
|
2018-09-13 04:16:22 +08:00
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_pragma_query_row() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2022-11-11 23:20:39 +08:00
|
|
|
assert_eq!("memory", db.one_column::<String>("PRAGMA journal_mode")?);
|
|
|
|
let mode = db.one_column::<String>("PRAGMA journal_mode=off")?;
|
2022-04-04 01:48:56 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
Ok(())
|
2018-09-13 04:16:22 +08:00
|
|
|
}
|
|
|
|
|
2014-10-20 07:56:41 +08:00
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_prepare_failures() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2020-11-06 05:14:00 +08:00
|
|
|
db.execute_batch("CREATE TABLE foo(x INTEGER);")?;
|
2014-10-20 07:56:41 +08:00
|
|
|
|
|
|
|
let err = db.prepare("SELECT * FROM does_not_exist").unwrap_err();
|
2022-10-29 01:02:49 +08:00
|
|
|
assert!(format!("{err}").contains("does_not_exist"));
|
2020-11-06 05:14:00 +08:00
|
|
|
Ok(())
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_last_insert_rowid() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2020-11-06 05:14:00 +08:00
|
|
|
db.execute_batch("CREATE TABLE foo(x INTEGER PRIMARY KEY)")?;
|
|
|
|
db.execute_batch("INSERT INTO foo DEFAULT VALUES")?;
|
2014-10-20 07:56:41 +08:00
|
|
|
|
|
|
|
assert_eq!(db.last_insert_rowid(), 1);
|
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
let mut stmt = db.prepare("INSERT INTO foo DEFAULT VALUES")?;
|
2015-12-11 05:48:09 +08:00
|
|
|
for _ in 0i32..9 {
|
2020-11-06 05:14:00 +08:00
|
|
|
stmt.execute([])?;
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
|
|
|
assert_eq!(db.last_insert_rowid(), 10);
|
2020-11-06 05:14:00 +08:00
|
|
|
Ok(())
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|
2015-09-21 08:44:51 +08:00
|
|
|
|
2018-05-13 00:35:08 +08:00
|
|
|
#[test]
|
2021-07-04 22:04:13 +08:00
|
|
|
fn test_is_autocommit() -> Result<()> {
|
|
|
|
let db = Connection::open_in_memory()?;
|
2018-08-11 18:48:21 +08:00
|
|
|
assert!(
|
|
|
|
db.is_autocommit(),
|
|
|
|
"autocommit expected to be active by default"
|
|
|
|
);
|
2021-07-04 22:04:13 +08:00
|
|
|
Ok(())
|
2018-05-13 00:35:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_is_busy() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2018-05-13 00:35:08 +08:00
|
|
|
assert!(!db.is_busy());
|
2020-11-06 05:14:00 +08:00
|
|
|
let mut stmt = db.prepare("PRAGMA schema_version")?;
|
2018-05-13 00:35:08 +08:00
|
|
|
assert!(!db.is_busy());
|
|
|
|
{
|
2020-11-06 05:14:00 +08:00
|
|
|
let mut rows = stmt.query([])?;
|
2018-05-13 00:35:08 +08:00
|
|
|
assert!(!db.is_busy());
|
2020-11-06 05:14:00 +08:00
|
|
|
let row = rows.next()?;
|
2018-05-13 00:35:08 +08:00
|
|
|
assert!(db.is_busy());
|
|
|
|
assert!(row.is_some());
|
|
|
|
}
|
|
|
|
assert!(!db.is_busy());
|
2020-11-06 05:14:00 +08:00
|
|
|
Ok(())
|
2018-05-13 00:35:08 +08:00
|
|
|
}
|
|
|
|
|
2015-09-21 08:44:51 +08:00
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_statement_debugging() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2015-09-21 08:44:51 +08:00
|
|
|
let query = "SELECT 12345";
|
2020-11-06 05:14:00 +08:00
|
|
|
let stmt = db.prepare(query)?;
|
2015-09-21 08:44:51 +08:00
|
|
|
|
2022-10-29 01:02:49 +08:00
|
|
|
assert!(format!("{stmt:?}").contains(query));
|
2020-11-06 05:14:00 +08:00
|
|
|
Ok(())
|
2015-09-21 08:44:51 +08:00
|
|
|
}
|
2015-09-21 09:28:50 +08:00
|
|
|
|
2015-12-13 12:10:35 +08:00
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_notnull_constraint_error() -> Result<()> {
|
2017-04-06 01:22:55 +08:00
|
|
|
// 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.
|
|
|
|
fn check_extended_code(extended_code: c_int) {
|
|
|
|
assert_eq!(extended_code, ffi::SQLITE_CONSTRAINT_NOTNULL);
|
|
|
|
}
|
|
|
|
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2020-11-06 05:14:00 +08:00
|
|
|
db.execute_batch("CREATE TABLE foo(x NOT NULL)")?;
|
2015-12-13 12:10:35 +08:00
|
|
|
|
2020-11-03 15:34:08 +08:00
|
|
|
let result = db.execute("INSERT INTO foo (x) VALUES (NULL)", []);
|
2015-12-13 12:10:35 +08:00
|
|
|
|
2015-12-13 13:54:08 +08:00
|
|
|
match result.unwrap_err() {
|
2015-12-16 03:41:54 +08:00
|
|
|
Error::SqliteFailure(err, _) => {
|
2017-02-04 18:33:23 +08:00
|
|
|
assert_eq!(err.code, ErrorCode::ConstraintViolation);
|
2017-04-06 01:22:55 +08:00
|
|
|
check_extended_code(err.extended_code);
|
2016-02-03 02:12:00 +08:00
|
|
|
}
|
2015-12-13 13:54:08 +08:00
|
|
|
err => panic!("Unexpected error {}", err),
|
|
|
|
}
|
2020-11-06 05:14:00 +08:00
|
|
|
Ok(())
|
2015-12-13 12:10:35 +08:00
|
|
|
}
|
|
|
|
|
2017-02-07 09:19:55 +08:00
|
|
|
#[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;
|
|
|
|
|
2022-10-29 01:02:49 +08:00
|
|
|
assert!(version().contains(&format!("{major}.{minor}.{patch}")));
|
2017-02-07 09:19:55 +08:00
|
|
|
}
|
|
|
|
|
2018-09-27 04:36:01 +08:00
|
|
|
#[test]
|
2018-10-28 16:54:30 +08:00
|
|
|
#[cfg(feature = "functions")]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_interrupt() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2018-09-27 04:36:01 +08:00
|
|
|
|
2018-10-28 16:54:30 +08:00
|
|
|
let interrupt_handle = db.get_interrupt_handle();
|
2018-09-27 04:36:01 +08:00
|
|
|
|
2020-01-27 01:11:11 +08:00
|
|
|
db.create_scalar_function(
|
|
|
|
"interrupt",
|
|
|
|
0,
|
2022-05-29 19:33:51 +08:00
|
|
|
functions::FunctionFlags::default(),
|
2020-01-27 01:11:11 +08:00
|
|
|
move |_| {
|
|
|
|
interrupt_handle.interrupt();
|
|
|
|
Ok(0)
|
|
|
|
},
|
2020-11-06 05:14:00 +08:00
|
|
|
)?;
|
2018-09-27 04:36:01 +08:00
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
let mut stmt =
|
|
|
|
db.prepare("SELECT interrupt() FROM (SELECT 1 UNION SELECT 2 UNION SELECT 3)")?;
|
2018-09-27 04:36:01 +08:00
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
let result: Result<Vec<i32>> = stmt.query([])?.map(|r| r.get(0)).collect();
|
2018-10-28 16:54:30 +08:00
|
|
|
|
2022-04-07 15:15:55 +08:00
|
|
|
assert_eq!(
|
|
|
|
result.unwrap_err().sqlite_error_code(),
|
|
|
|
Some(ErrorCode::OperationInterrupted)
|
|
|
|
);
|
2020-11-06 05:14:00 +08:00
|
|
|
Ok(())
|
2018-09-27 04:36:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[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.
|
|
|
|
}
|
|
|
|
|
2018-10-19 02:59:30 +08:00
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_get_raw() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2020-11-06 05:14:00 +08:00
|
|
|
db.execute_batch("CREATE TABLE foo(i, x);")?;
|
2018-10-19 02:59:30 +08:00
|
|
|
let vals = ["foobar", "1234", "qwerty"];
|
2020-11-06 05:14:00 +08:00
|
|
|
let mut insert_stmt = db.prepare("INSERT INTO foo(i, x) VALUES(?, ?)")?;
|
2018-10-19 02:59:30 +08:00
|
|
|
for (i, v) in vals.iter().enumerate() {
|
|
|
|
let i_to_insert = i as i64;
|
2020-11-06 05:14:00 +08:00
|
|
|
assert_eq!(insert_stmt.execute(params![i_to_insert, v])?, 1);
|
2018-10-19 02:59:30 +08:00
|
|
|
}
|
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
let mut query = db.prepare("SELECT i, x FROM foo")?;
|
|
|
|
let mut rows = query.query([])?;
|
2018-10-19 02:59:30 +08:00
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
while let Some(row) = rows.next()? {
|
2020-12-23 04:34:30 +08:00
|
|
|
let i = row.get_ref(0)?.as_i64()?;
|
2018-10-19 02:59:30 +08:00
|
|
|
let expect = vals[i as usize];
|
2020-12-23 04:34:30 +08:00
|
|
|
let x = row.get_ref("x")?.as_str()?;
|
2018-10-19 02:59:30 +08:00
|
|
|
assert_eq!(x, expect);
|
|
|
|
}
|
2020-12-23 04:34:30 +08:00
|
|
|
|
|
|
|
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]);
|
|
|
|
}
|
2020-11-06 05:14:00 +08:00
|
|
|
Ok(())
|
2018-10-19 02:59:30 +08:00
|
|
|
}
|
|
|
|
|
2019-01-06 19:58:46 +08:00
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_from_handle() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2019-01-06 19:58:46 +08:00
|
|
|
let handle = unsafe { db.handle() };
|
|
|
|
{
|
2020-11-06 05:14:00 +08:00
|
|
|
let db = unsafe { Connection::from_handle(handle) }?;
|
|
|
|
db.execute_batch("PRAGMA VACUUM")?;
|
2019-01-06 19:58:46 +08:00
|
|
|
}
|
|
|
|
db.close().unwrap();
|
2020-11-06 05:14:00 +08:00
|
|
|
Ok(())
|
2019-01-06 19:58:46 +08:00
|
|
|
}
|
|
|
|
|
2015-09-21 09:28:50 +08:00
|
|
|
mod query_and_then_tests {
|
2018-12-08 04:57:04 +08:00
|
|
|
|
2015-09-21 09:28:50 +08:00
|
|
|
use super::*;
|
|
|
|
|
2015-12-13 13:54:08 +08:00
|
|
|
#[derive(Debug)]
|
2015-09-21 09:28:50 +08:00
|
|
|
enum CustomError {
|
|
|
|
SomeError,
|
2015-12-13 02:53:58 +08:00
|
|
|
Sqlite(Error),
|
2015-09-21 09:28:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for CustomError {
|
2020-04-07 05:43:06 +08:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
2015-09-21 09:28:50 +08:00
|
|
|
match *self {
|
2020-01-26 23:57:58 +08:00
|
|
|
CustomError::SomeError => write!(f, "my custom error"),
|
2022-10-29 01:02:49 +08:00
|
|
|
CustomError::Sqlite(ref se) => write!(f, "my custom error: {se}"),
|
2015-09-21 09:28:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl StdError for CustomError {
|
2015-12-11 05:48:09 +08:00
|
|
|
fn description(&self) -> &str {
|
|
|
|
"my custom error"
|
|
|
|
}
|
2018-08-17 00:29:46 +08:00
|
|
|
|
2018-12-08 04:57:04 +08:00
|
|
|
fn cause(&self) -> Option<&dyn StdError> {
|
2015-09-21 09:28:50 +08:00
|
|
|
match *self {
|
2015-12-11 05:48:09 +08:00
|
|
|
CustomError::SomeError => None,
|
2015-09-21 09:28:50 +08:00
|
|
|
CustomError::Sqlite(ref se) => Some(se),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-13 02:53:58 +08:00
|
|
|
impl From<Error> for CustomError {
|
|
|
|
fn from(se: Error) -> CustomError {
|
2015-09-21 09:28:50 +08:00
|
|
|
CustomError::Sqlite(se)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-07 05:43:06 +08:00
|
|
|
type CustomResult<T> = Result<T, CustomError>;
|
2015-09-21 09:28:50 +08:00
|
|
|
|
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_query_and_then() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2015-09-21 09:28:50 +08:00
|
|
|
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;";
|
2020-11-06 05:14:00 +08:00
|
|
|
db.execute_batch(sql)?;
|
2015-09-21 09:28:50 +08:00
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
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();
|
2015-09-21 09:28:50 +08:00
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
assert_eq!(results?.concat(), "hello, world!");
|
|
|
|
Ok(())
|
2015-09-21 09:28:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_query_and_then_fails() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2015-09-21 09:28:50 +08:00
|
|
|
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;";
|
2020-11-06 05:14:00 +08:00
|
|
|
db.execute_batch(sql)?;
|
2015-09-21 09:28:50 +08:00
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
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();
|
2015-09-21 09:28:50 +08:00
|
|
|
|
2015-12-13 13:54:08 +08:00
|
|
|
match bad_type.unwrap_err() {
|
2019-11-03 18:19:07 +08:00
|
|
|
Error::InvalidColumnType(..) => (),
|
2015-12-13 13:54:08 +08:00
|
|
|
err => panic!("Unexpected error {}", err),
|
|
|
|
}
|
2015-09-21 09:28:50 +08:00
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
let bad_idx: Result<Vec<String>> =
|
|
|
|
query.query_and_then([], |row| row.get(3))?.collect();
|
2015-09-21 09:28:50 +08:00
|
|
|
|
2015-12-13 13:54:08 +08:00
|
|
|
match bad_idx.unwrap_err() {
|
|
|
|
Error::InvalidColumnIndex(_) => (),
|
|
|
|
err => panic!("Unexpected error {}", err),
|
|
|
|
}
|
2020-11-06 05:14:00 +08:00
|
|
|
Ok(())
|
2015-09-21 09:28:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_query_and_then_custom_error() -> CustomResult<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2015-09-21 09:28:50 +08:00
|
|
|
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;";
|
2020-11-06 05:14:00 +08:00
|
|
|
db.execute_batch(sql)?;
|
2015-09-21 09:28:50 +08:00
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC")?;
|
2018-08-11 18:48:21 +08:00
|
|
|
let results: CustomResult<Vec<String>> = query
|
2020-11-06 05:14:00 +08:00
|
|
|
.query_and_then([], |row| row.get(1).map_err(CustomError::Sqlite))?
|
2015-09-21 09:28:50 +08:00
|
|
|
.collect();
|
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
assert_eq!(results?.concat(), "hello, world!");
|
|
|
|
Ok(())
|
2015-09-21 09:28:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_query_and_then_custom_error_fails() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2015-09-21 09:28:50 +08:00
|
|
|
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;";
|
2020-11-06 05:14:00 +08:00
|
|
|
db.execute_batch(sql)?;
|
2015-09-21 09:28:50 +08:00
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC")?;
|
2018-08-11 18:48:21 +08:00
|
|
|
let bad_type: CustomResult<Vec<f64>> = query
|
2020-11-06 05:14:00 +08:00
|
|
|
.query_and_then([], |row| row.get(1).map_err(CustomError::Sqlite))?
|
2015-09-21 09:28:50 +08:00
|
|
|
.collect();
|
|
|
|
|
2015-12-13 13:54:08 +08:00
|
|
|
match bad_type.unwrap_err() {
|
2019-11-03 18:19:07 +08:00
|
|
|
CustomError::Sqlite(Error::InvalidColumnType(..)) => (),
|
2015-12-13 13:54:08 +08:00
|
|
|
err => panic!("Unexpected error {}", err),
|
|
|
|
}
|
2015-09-21 09:28:50 +08:00
|
|
|
|
2018-08-11 18:48:21 +08:00
|
|
|
let bad_idx: CustomResult<Vec<String>> = query
|
2020-11-06 05:14:00 +08:00
|
|
|
.query_and_then([], |row| row.get(3).map_err(CustomError::Sqlite))?
|
2015-09-21 09:28:50 +08:00
|
|
|
.collect();
|
|
|
|
|
2015-12-13 13:54:08 +08:00
|
|
|
match bad_idx.unwrap_err() {
|
|
|
|
CustomError::Sqlite(Error::InvalidColumnIndex(_)) => (),
|
|
|
|
err => panic!("Unexpected error {}", err),
|
|
|
|
}
|
2015-09-21 09:28:50 +08:00
|
|
|
|
2018-08-11 18:48:21 +08:00
|
|
|
let non_sqlite_err: CustomResult<Vec<String>> = query
|
2020-11-06 05:14:00 +08:00
|
|
|
.query_and_then([], |_| Err(CustomError::SomeError))?
|
2015-09-21 09:28:50 +08:00
|
|
|
.collect();
|
|
|
|
|
2015-12-13 13:54:08 +08:00
|
|
|
match non_sqlite_err.unwrap_err() {
|
|
|
|
CustomError::SomeError => (),
|
|
|
|
err => panic!("Unexpected error {}", err),
|
|
|
|
}
|
2020-11-06 05:14:00 +08:00
|
|
|
Ok(())
|
2015-09-21 09:28:50 +08:00
|
|
|
}
|
|
|
|
|
2015-09-21 09:30:40 +08:00
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_query_row_and_then_custom_error() -> CustomResult<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2015-09-21 09:30:40 +08:00
|
|
|
let sql = "BEGIN;
|
|
|
|
CREATE TABLE foo(x INTEGER, y TEXT);
|
|
|
|
INSERT INTO foo VALUES(4, \"hello\");
|
|
|
|
END;";
|
2020-11-06 05:14:00 +08:00
|
|
|
db.execute_batch(sql)?;
|
2015-09-21 09:30:40 +08:00
|
|
|
|
|
|
|
let query = "SELECT x, y FROM foo ORDER BY x DESC";
|
2020-11-03 15:34:08 +08:00
|
|
|
let results: CustomResult<String> =
|
|
|
|
db.query_row_and_then(query, [], |row| row.get(1).map_err(CustomError::Sqlite));
|
2015-09-21 09:30:40 +08:00
|
|
|
|
2020-11-06 05:14:00 +08:00
|
|
|
assert_eq!(results?, "hello");
|
|
|
|
Ok(())
|
2015-09-21 09:30:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_query_row_and_then_custom_error_fails() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2015-09-21 09:30:40 +08:00
|
|
|
let sql = "BEGIN;
|
|
|
|
CREATE TABLE foo(x INTEGER, y TEXT);
|
|
|
|
INSERT INTO foo VALUES(4, \"hello\");
|
|
|
|
END;";
|
2020-11-06 05:14:00 +08:00
|
|
|
db.execute_batch(sql)?;
|
2015-09-21 09:30:40 +08:00
|
|
|
|
|
|
|
let query = "SELECT x, y FROM foo ORDER BY x DESC";
|
2020-11-03 15:34:08 +08:00
|
|
|
let bad_type: CustomResult<f64> =
|
|
|
|
db.query_row_and_then(query, [], |row| row.get(1).map_err(CustomError::Sqlite));
|
2015-09-21 09:30:40 +08:00
|
|
|
|
2015-12-13 13:54:08 +08:00
|
|
|
match bad_type.unwrap_err() {
|
2019-11-03 18:19:07 +08:00
|
|
|
CustomError::Sqlite(Error::InvalidColumnType(..)) => (),
|
2015-12-13 13:54:08 +08:00
|
|
|
err => panic!("Unexpected error {}", err),
|
|
|
|
}
|
2015-09-21 09:30:40 +08:00
|
|
|
|
2020-11-03 15:34:08 +08:00
|
|
|
let bad_idx: CustomResult<String> =
|
|
|
|
db.query_row_and_then(query, [], |row| row.get(3).map_err(CustomError::Sqlite));
|
2015-09-21 09:30:40 +08:00
|
|
|
|
2015-12-13 13:54:08 +08:00
|
|
|
match bad_idx.unwrap_err() {
|
|
|
|
CustomError::Sqlite(Error::InvalidColumnIndex(_)) => (),
|
|
|
|
err => panic!("Unexpected error {}", err),
|
|
|
|
}
|
2015-09-21 09:30:40 +08:00
|
|
|
|
2018-08-11 18:48:21 +08:00
|
|
|
let non_sqlite_err: CustomResult<String> =
|
2020-11-03 15:34:08 +08:00
|
|
|
db.query_row_and_then(query, [], |_| Err(CustomError::SomeError));
|
2015-09-21 09:30:40 +08:00
|
|
|
|
2015-12-13 13:54:08 +08:00
|
|
|
match non_sqlite_err.unwrap_err() {
|
|
|
|
CustomError::SomeError => (),
|
|
|
|
err => panic!("Unexpected error {}", err),
|
|
|
|
}
|
2020-11-06 05:14:00 +08:00
|
|
|
Ok(())
|
2015-09-21 09:30:40 +08:00
|
|
|
}
|
2020-10-31 18:11:12 +08:00
|
|
|
}
|
2015-09-21 09:30:40 +08:00
|
|
|
|
2020-10-31 18:11:12 +08:00
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_dynamic() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2020-10-31 18:11:12 +08:00
|
|
|
let sql = "BEGIN;
|
2016-01-02 17:28:00 +08:00
|
|
|
CREATE TABLE foo(x INTEGER, y TEXT);
|
|
|
|
INSERT INTO foo VALUES(4, \"hello\");
|
|
|
|
END;";
|
2020-11-06 05:14:00 +08:00
|
|
|
db.execute_batch(sql)?;
|
2016-01-02 17:28:00 +08:00
|
|
|
|
2020-11-03 15:34:08 +08:00
|
|
|
db.query_row("SELECT * FROM foo", [], |r| {
|
2021-05-26 20:51:28 +08:00
|
|
|
assert_eq!(2, r.as_ref().column_count());
|
2020-10-31 18:11:12 +08:00
|
|
|
Ok(())
|
|
|
|
})
|
|
|
|
}
|
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_dyn_box() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2020-11-06 05:14:00 +08:00
|
|
|
db.execute_batch("CREATE TABLE foo(x INTEGER);")?;
|
2020-10-31 18:11:12 +08:00
|
|
|
let b: Box<dyn ToSql> = Box::new(5);
|
2020-11-06 05:14:00 +08:00
|
|
|
db.execute("INSERT INTO foo VALUES(?)", [b])?;
|
2020-11-03 15:34:08 +08:00
|
|
|
db.query_row("SELECT x FROM foo", [], |r| {
|
2020-10-31 18:11:12 +08:00
|
|
|
assert_eq!(5, r.get_unwrap::<_, i32>(0));
|
|
|
|
Ok(())
|
|
|
|
})
|
|
|
|
}
|
2020-01-19 02:04:28 +08:00
|
|
|
|
2020-10-31 18:11:12 +08:00
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_params() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2020-10-31 18:11:12 +08:00
|
|
|
db.query_row(
|
|
|
|
"SELECT
|
2020-01-19 02:04:28 +08:00
|
|
|
?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
|
|
|
?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
|
|
|
?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
|
|
|
?, ?, ?, ?;",
|
2020-10-31 18:11:12 +08:00
|
|
|
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(())
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
2020-03-05 03:26:31 +08:00
|
|
|
|
2020-10-31 18:11:12 +08:00
|
|
|
#[test]
|
|
|
|
#[cfg(not(feature = "extra_check"))]
|
2020-11-06 05:24:55 +08:00
|
|
|
fn test_alter_table() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2020-11-06 05:14:00 +08:00
|
|
|
db.execute_batch("CREATE TABLE x(t);")?;
|
2020-10-31 18:11:12 +08:00
|
|
|
// `execute_batch` should be used but `execute` should also work
|
2020-11-06 05:24:55 +08:00
|
|
|
db.execute("ALTER TABLE x RENAME TO y;", [])?;
|
|
|
|
Ok(())
|
2020-10-31 18:11:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2020-11-06 05:14:00 +08:00
|
|
|
fn test_batch() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2020-10-31 18:11:12 +08:00
|
|
|
let sql = r"
|
|
|
|
CREATE TABLE tbl1 (col);
|
|
|
|
CREATE TABLE tbl2 (col);
|
|
|
|
";
|
|
|
|
let batch = Batch::new(&db, sql);
|
|
|
|
for stmt in batch {
|
2020-11-06 05:14:00 +08:00
|
|
|
let mut stmt = stmt?;
|
|
|
|
stmt.execute([])?;
|
2020-03-05 03:26:31 +08:00
|
|
|
}
|
2020-11-06 05:14:00 +08:00
|
|
|
Ok(())
|
2015-09-21 09:28:50 +08:00
|
|
|
}
|
2021-04-04 18:47:21 +08:00
|
|
|
|
|
|
|
#[test]
|
2022-04-21 19:34:20 +08:00
|
|
|
#[cfg(feature = "modern_sqlite")]
|
2021-04-04 18:47:21 +08:00
|
|
|
fn test_returning() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2021-04-04 18:47:21 +08:00
|
|
|
db.execute_batch("CREATE TABLE foo(x INTEGER PRIMARY KEY)")?;
|
2022-11-11 23:20:39 +08:00
|
|
|
let row_id = db.one_column::<i64>("INSERT INTO foo DEFAULT VALUES RETURNING ROWID")?;
|
2021-04-04 18:47:21 +08:00
|
|
|
assert_eq!(row_id, 1);
|
|
|
|
Ok(())
|
|
|
|
}
|
2021-07-03 23:03:58 +08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cache_flush() -> Result<()> {
|
2021-07-04 22:04:13 +08:00
|
|
|
let db = Connection::open_in_memory()?;
|
2021-07-03 23:03:58 +08:00
|
|
|
db.cache_flush()
|
|
|
|
}
|
2022-03-18 02:58:02 +08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
pub fn db_readonly() -> Result<()> {
|
|
|
|
let db = Connection::open_in_memory()?;
|
2022-05-29 19:33:51 +08:00
|
|
|
assert!(!db.is_readonly(MAIN_DB)?);
|
2022-03-18 02:58:02 +08:00
|
|
|
Ok(())
|
|
|
|
}
|
2014-10-20 07:56:41 +08:00
|
|
|
}
|