Overhaul query API, removing the need for the _named variants of all functions, and rusqlite::NO_PARAMS

This commit is contained in:
Thom Chiovoloni 2020-11-02 23:34:08 -08:00
parent c45446ba73
commit 032aea73b8
10 changed files with 680 additions and 328 deletions

View File

@ -138,7 +138,7 @@
//! // rust (potentially with a dynamic size). //! // rust (potentially with a dynamic size).
//! db.execute( //! db.execute(
//! "INSERT INTO test_table (content) VALUES (?)", //! "INSERT INTO test_table (content) VALUES (?)",
//! &[ZeroBlob(64)], //! [ZeroBlob(64)],
//! )?; //! )?;
//! //!
//! // given a new row ID, we can reopen the blob on that row //! // given a new row ID, we can reopen the blob on that row
@ -182,7 +182,7 @@
//! // rust (potentially with a dynamic size). //! // rust (potentially with a dynamic size).
//! db.execute( //! db.execute(
//! "INSERT INTO test_table (content) VALUES (?)", //! "INSERT INTO test_table (content) VALUES (?)",
//! &[ZeroBlob(64)], //! [ZeroBlob(64)],
//! )?; //! )?;
//! //!
//! // given a new row ID, we can reopen the blob on that row //! // given a new row ID, we can reopen the blob on that row

View File

@ -45,7 +45,7 @@
//! //!
//! let is_match: bool = db.query_row( //! let is_match: bool = db.query_row(
//! "SELECT regexp('[aeiou]*', 'aaaaeeeiii')", //! "SELECT regexp('[aeiou]*', 'aaaaeeeiii')",
//! NO_PARAMS, //! [],
//! |row| row.get(0), //! |row| row.get(0),
//! )?; //! )?;
//! //!
@ -311,7 +311,7 @@ impl Connection {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// # use rusqlite::{Connection, Result, NO_PARAMS}; /// # use rusqlite::{Connection, Result};
/// # use rusqlite::functions::FunctionFlags; /// # use rusqlite::functions::FunctionFlags;
/// fn scalar_function_example(db: Connection) -> Result<()> { /// fn scalar_function_example(db: Connection) -> Result<()> {
/// db.create_scalar_function( /// db.create_scalar_function(
@ -324,7 +324,7 @@ impl Connection {
/// }, /// },
/// )?; /// )?;
/// ///
/// let six_halved: f64 = db.query_row("SELECT halve(6)", NO_PARAMS, |r| r.get(0))?; /// let six_halved: f64 = db.query_row("SELECT halve(6)", [], |r| r.get(0))?;
/// assert_eq!(six_halved, 3f64); /// assert_eq!(six_halved, 3f64);
/// Ok(()) /// Ok(())
/// } /// }

View File

@ -77,6 +77,7 @@ pub use crate::ffi::ErrorCode;
pub use crate::hooks::Action; pub use crate::hooks::Action;
#[cfg(feature = "load_extension")] #[cfg(feature = "load_extension")]
pub use crate::load_extension_guard::LoadExtensionGuard; pub use crate::load_extension_guard::LoadExtensionGuard;
pub use crate::params::{params_from_iter, Params, ParamsFromIter};
pub use crate::row::{AndThenRows, Map, MappedRows, Row, RowIndex, Rows}; pub use crate::row::{AndThenRows, Map, MappedRows, Row, RowIndex, Rows};
pub use crate::statement::{Statement, StatementStatus}; pub use crate::statement::{Statement, StatementStatus};
pub use crate::transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior}; pub use crate::transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior};
@ -107,6 +108,7 @@ mod inner_connection;
pub mod limits; pub mod limits;
#[cfg(feature = "load_extension")] #[cfg(feature = "load_extension")]
mod load_extension_guard; mod load_extension_guard;
mod params;
mod pragma; mod pragma;
mod raw_statement; mod raw_statement;
mod row; mod row;
@ -127,11 +129,20 @@ pub(crate) use util::SmallCString;
// Number of cached prepared statements we'll hold on to. // Number of cached prepared statements we'll hold on to.
const STATEMENT_CACHE_DEFAULT_CAPACITY: usize = 16; const STATEMENT_CACHE_DEFAULT_CAPACITY: usize = 16;
/// To be used when your statement has no [parameter](https://sqlite.org/lang_expr.html#varparam). /// To be used when your statement has no [parameter][sqlite-varparam].
///
/// [sqlite-varparam]: https://sqlite.org/lang_expr.html#varparam
///
/// Note that for most uses this is no longer necessary, and an empty array
/// should be used instead.
///
/// That is, in previous rusqlite releases you would need to do
/// `conn.execute(rusqlite::NO_PARAMS)`, however you can now do
/// `conn.execute([])` which is equivalent.
pub const NO_PARAMS: &[&dyn ToSql] = &[]; pub const NO_PARAMS: &[&dyn ToSql] = &[];
/// A macro making it more convenient to pass heterogeneous lists /// A macro making it more convenient to pass heterogeneous or long lists of
/// of parameters as a `&[&dyn ToSql]`. /// parameters as a `&[&dyn ToSql]`.
/// ///
/// # Example /// # Example
/// ///
@ -191,12 +202,12 @@ macro_rules! params {
#[macro_export] #[macro_export]
macro_rules! named_params { macro_rules! named_params {
() => { () => {
&[] (&[] as &[(&str, &dyn $crate::ToSql)])
}; };
// Note: It's a lot more work to support this as part of the same macro as // Note: It's a lot more work to support this as part of the same macro as
// `params!`, unfortunately. // `params!`, unfortunately.
($($param_name:literal: $param_val:expr),+ $(,)?) => { ($($param_name:literal: $param_val:expr),+ $(,)?) => {
&[$(($param_name, &$param_val as &dyn $crate::ToSql)),+] (&[$(($param_name, &$param_val as &dyn $crate::ToSql)),+] as &[(&str, &dyn $crate::ToSql)])
}; };
} }
@ -471,25 +482,47 @@ impl Connection {
/// ///
/// ## Example /// ## Example
/// ///
/// ### With positional params
///
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Connection}; /// # use rusqlite::{Connection};
/// fn update_rows(conn: &Connection) { /// fn update_rows(conn: &Connection) {
/// match conn.execute("UPDATE foo SET bar = 'baz' WHERE qux = ?", &[1i32]) { /// match conn.execute("UPDATE foo SET bar = 'baz' WHERE qux = ?", [1i32]) {
/// Ok(updated) => println!("{} rows were updated", updated), /// Ok(updated) => println!("{} rows were updated", updated),
/// Err(err) => println!("update failed: {}", err), /// Err(err) => println!("update failed: {}", err),
/// } /// }
/// } /// }
/// ``` /// ```
/// ///
/// ### With positional params of varying types
///
/// ```rust,no_run
/// # use rusqlite::{Connection};
/// fn update_rows(conn: &Connection) {
/// match conn.execute("UPDATE foo SET bar = 'baz' WHERE qux = ?", [1i32]) {
/// Ok(updated) => println!("{} rows were updated", updated),
/// Err(err) => println!("update failed: {}", err),
/// }
/// }
/// ```
///
/// ### With named params
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn insert(conn: &Connection) -> Result<usize> {
/// conn.execute_named(
/// "INSERT INTO test (name) VALUES (:name)",
/// rusqlite::named_params!{ ":name": "one" },
/// )
/// }
/// ```
///
/// # Failure /// # Failure
/// ///
/// Will return `Err` if `sql` cannot be converted to a C-compatible string /// Will return `Err` if `sql` cannot be converted to a C-compatible string
/// or if the underlying SQLite call fails. /// or if the underlying SQLite call fails.
pub fn execute<P>(&self, sql: &str, params: P) -> Result<usize> pub fn execute<P: Params>(&self, sql: &str, params: P) -> Result<usize> {
where
P: IntoIterator,
P::Item: ToSql,
{
self.prepare(sql) self.prepare(sql)
.and_then(|mut stmt| stmt.check_no_tail().and_then(|_| stmt.execute(params))) .and_then(|mut stmt| stmt.check_no_tail().and_then(|_| stmt.execute(params)))
} }
@ -500,23 +533,14 @@ impl Connection {
/// On success, returns the number of rows that were changed or inserted or /// On success, returns the number of rows that were changed or inserted or
/// deleted (via `sqlite3_changes`). /// deleted (via `sqlite3_changes`).
/// ///
/// ## Example
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn insert(conn: &Connection) -> Result<usize> {
/// conn.execute_named(
/// "INSERT INTO test (name) VALUES (:name)",
/// &[(":name", &"one")],
/// )
/// }
/// ```
///
/// # Failure /// # Failure
/// ///
/// Will return `Err` if `sql` cannot be converted to a C-compatible string /// Will return `Err` if `sql` cannot be converted to a C-compatible string
/// or if the underlying SQLite call fails. /// or if the underlying SQLite call fails.
#[deprecated = "You can use `execute` with named params now."]
pub fn execute_named(&self, sql: &str, params: &[(&str, &dyn ToSql)]) -> Result<usize> { pub fn execute_named(&self, sql: &str, params: &[(&str, &dyn ToSql)]) -> Result<usize> {
// This function itself is deprecated, so it's fine
#![allow(deprecated)]
self.prepare(sql).and_then(|mut stmt| { self.prepare(sql).and_then(|mut stmt| {
stmt.check_no_tail() stmt.check_no_tail()
.and_then(|_| stmt.execute_named(params)) .and_then(|_| stmt.execute_named(params))
@ -537,11 +561,11 @@ impl Connection {
/// ## Example /// ## Example
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Result,Connection, NO_PARAMS}; /// # use rusqlite::{Result, Connection};
/// fn preferred_locale(conn: &Connection) -> Result<String> { /// fn preferred_locale(conn: &Connection) -> Result<String> {
/// conn.query_row( /// conn.query_row(
/// "SELECT value FROM preferences WHERE name='locale'", /// "SELECT value FROM preferences WHERE name='locale'",
/// NO_PARAMS, /// [],
/// |row| row.get(0), /// |row| row.get(0),
/// ) /// )
/// } /// }
@ -560,8 +584,7 @@ impl Connection {
/// or if the underlying SQLite call fails. /// or if the underlying SQLite call fails.
pub fn query_row<T, P, F>(&self, sql: &str, params: P, f: F) -> Result<T> pub fn query_row<T, P, F>(&self, sql: &str, params: P, f: F) -> Result<T>
where where
P: IntoIterator, P: Params,
P::Item: ToSql,
F: FnOnce(&Row<'_>) -> Result<T>, F: FnOnce(&Row<'_>) -> Result<T>,
{ {
let mut stmt = self.prepare(sql)?; let mut stmt = self.prepare(sql)?;
@ -583,13 +606,12 @@ impl Connection {
/// ///
/// Will return `Err` if `sql` cannot be converted to a C-compatible string /// Will return `Err` if `sql` cannot be converted to a C-compatible string
/// or if the underlying SQLite call fails. /// or if the underlying SQLite call fails.
#[deprecated = "You can use `query_row` with named params now."]
pub fn query_row_named<T, F>(&self, sql: &str, params: &[(&str, &dyn ToSql)], f: F) -> Result<T> pub fn query_row_named<T, F>(&self, sql: &str, params: &[(&str, &dyn ToSql)], f: F) -> Result<T>
where where
F: FnOnce(&Row<'_>) -> Result<T>, F: FnOnce(&Row<'_>) -> Result<T>,
{ {
let mut stmt = self.prepare(sql)?; self.query_row(sql, params, f)
stmt.check_no_tail()?;
stmt.query_row_named(params, f)
} }
/// Convenience method to execute a query that is expected to return a /// Convenience method to execute a query that is expected to return a
@ -600,11 +622,11 @@ impl Connection {
/// ## Example /// ## Example
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Result,Connection, NO_PARAMS}; /// # use rusqlite::{Result, Connection};
/// fn preferred_locale(conn: &Connection) -> Result<String> { /// fn preferred_locale(conn: &Connection) -> Result<String> {
/// conn.query_row_and_then( /// conn.query_row_and_then(
/// "SELECT value FROM preferences WHERE name='locale'", /// "SELECT value FROM preferences WHERE name='locale'",
/// NO_PARAMS, /// [],
/// |row| row.get(0), /// |row| row.get(0),
/// ) /// )
/// } /// }
@ -619,8 +641,7 @@ impl Connection {
/// or if the underlying SQLite call fails. /// or if the underlying SQLite call fails.
pub fn query_row_and_then<T, E, P, F>(&self, sql: &str, params: P, f: F) -> Result<T, E> pub fn query_row_and_then<T, E, P, F>(&self, sql: &str, params: P, f: F) -> Result<T, E>
where where
P: IntoIterator, P: Params,
P::Item: ToSql,
F: FnOnce(&Row<'_>) -> Result<T, E>, F: FnOnce(&Row<'_>) -> Result<T, E>,
E: convert::From<Error>, E: convert::From<Error>,
{ {
@ -1039,13 +1060,13 @@ mod test {
let tx2 = db2.transaction().unwrap(); let tx2 = db2.transaction().unwrap();
// SELECT first makes sqlite lock with a shared lock // SELECT first makes sqlite lock with a shared lock
tx1.query_row("SELECT x FROM foo LIMIT 1", NO_PARAMS, |_| Ok(())) tx1.query_row("SELECT x FROM foo LIMIT 1", [], |_| Ok(()))
.unwrap(); .unwrap();
tx2.query_row("SELECT x FROM foo LIMIT 1", NO_PARAMS, |_| Ok(())) tx2.query_row("SELECT x FROM foo LIMIT 1", [], |_| Ok(()))
.unwrap(); .unwrap();
tx1.execute("INSERT INTO foo VALUES(?1)", &[1]).unwrap(); tx1.execute("INSERT INTO foo VALUES(?1)", &[&1]).unwrap();
let _ = tx2.execute("INSERT INTO foo VALUES(?1)", &[2]); let _ = tx2.execute("INSERT INTO foo VALUES(?1)", [2]);
let _ = tx1.commit(); let _ = tx1.commit();
let _ = tx2.commit(); let _ = tx2.commit();
@ -1075,7 +1096,7 @@ mod test {
let path_string = path.to_str().unwrap(); let path_string = path.to_str().unwrap();
let db = Connection::open(&path_string).unwrap(); let db = Connection::open(&path_string).unwrap();
let the_answer: Result<i64> = db.query_row("SELECT x FROM foo", NO_PARAMS, |r| r.get(0)); let the_answer: Result<i64> = db.query_row("SELECT x FROM foo", [], |r| r.get(0));
assert_eq!(42i64, the_answer.unwrap()); assert_eq!(42i64, the_answer.unwrap());
} }
@ -1132,7 +1153,7 @@ mod test {
} }
let db = Connection::open(&db_path).unwrap(); let db = Connection::open(&db_path).unwrap();
let the_answer: Result<i64> = db.query_row("SELECT x FROM foo", NO_PARAMS, |r| r.get(0)); let the_answer: Result<i64> = db.query_row("SELECT x FROM foo", [], |r| r.get(0));
assert_eq!(42i64, the_answer.unwrap()); assert_eq!(42i64, the_answer.unwrap());
} }
@ -1214,18 +1235,16 @@ mod test {
assert_eq!( assert_eq!(
1, 1,
db.execute("INSERT INTO foo(x) VALUES (?)", &[1i32]) db.execute("INSERT INTO foo(x) VALUES (?)", [1i32]).unwrap()
.unwrap()
); );
assert_eq!( assert_eq!(
1, 1,
db.execute("INSERT INTO foo(x) VALUES (?)", &[2i32]) db.execute("INSERT INTO foo(x) VALUES (?)", [2i32]).unwrap()
.unwrap()
); );
assert_eq!( assert_eq!(
3i32, 3i32,
db.query_row::<i32, _, _>("SELECT SUM(x) FROM foo", NO_PARAMS, |r| r.get(0)) db.query_row::<i32, _, _>("SELECT SUM(x) FROM foo", [], |r| r.get(0))
.unwrap() .unwrap()
); );
} }
@ -1234,7 +1253,7 @@ mod test {
#[cfg(feature = "extra_check")] #[cfg(feature = "extra_check")]
fn test_execute_select() { fn test_execute_select() {
let db = checked_memory_handle(); let db = checked_memory_handle();
let err = db.execute("SELECT 1 WHERE 1 < ?", &[1i32]).unwrap_err(); let err = db.execute("SELECT 1 WHERE 1 < ?", [1i32]).unwrap_err();
if err != Error::ExecuteReturnedResults { if err != Error::ExecuteReturnedResults {
panic!("Unexpected error: {}", err); panic!("Unexpected error: {}", err);
} }
@ -1247,7 +1266,7 @@ mod test {
let err = db let err = db
.execute( .execute(
"CREATE TABLE foo(x INTEGER); CREATE TABLE foo(x INTEGER)", "CREATE TABLE foo(x INTEGER); CREATE TABLE foo(x INTEGER)",
NO_PARAMS, [],
) )
.unwrap_err(); .unwrap_err();
match err { match err {
@ -1276,18 +1295,18 @@ mod test {
db.execute_batch("CREATE TABLE foo(x INTEGER);").unwrap(); db.execute_batch("CREATE TABLE foo(x INTEGER);").unwrap();
let mut insert_stmt = db.prepare("INSERT INTO foo(x) VALUES(?)").unwrap(); let mut insert_stmt = db.prepare("INSERT INTO foo(x) VALUES(?)").unwrap();
assert_eq!(insert_stmt.execute(&[1i32]).unwrap(), 1); assert_eq!(insert_stmt.execute([1i32]).unwrap(), 1);
assert_eq!(insert_stmt.execute(&[2i32]).unwrap(), 1); assert_eq!(insert_stmt.execute([2i32]).unwrap(), 1);
assert_eq!(insert_stmt.execute(&[3i32]).unwrap(), 1); assert_eq!(insert_stmt.execute([3i32]).unwrap(), 1);
assert_eq!(insert_stmt.execute(&["hello".to_string()]).unwrap(), 1); assert_eq!(insert_stmt.execute(["hello".to_string()]).unwrap(), 1);
assert_eq!(insert_stmt.execute(&["goodbye".to_string()]).unwrap(), 1); assert_eq!(insert_stmt.execute(["goodbye".to_string()]).unwrap(), 1);
assert_eq!(insert_stmt.execute(&[types::Null]).unwrap(), 1); assert_eq!(insert_stmt.execute([types::Null]).unwrap(), 1);
let mut update_stmt = db.prepare("UPDATE foo SET x=? WHERE x<?").unwrap(); let mut update_stmt = db.prepare("UPDATE foo SET x=? WHERE x<?").unwrap();
assert_eq!(update_stmt.execute(&[3i32, 3i32]).unwrap(), 2); assert_eq!(update_stmt.execute([3i32, 3i32]).unwrap(), 2);
assert_eq!(update_stmt.execute(&[3i32, 3i32]).unwrap(), 0); assert_eq!(update_stmt.execute([3i32, 3i32]).unwrap(), 0);
assert_eq!(update_stmt.execute(&[8i32, 8i32]).unwrap(), 3); assert_eq!(update_stmt.execute([8i32, 8i32]).unwrap(), 3);
} }
#[test] #[test]
@ -1296,15 +1315,15 @@ mod test {
db.execute_batch("CREATE TABLE foo(x INTEGER);").unwrap(); db.execute_batch("CREATE TABLE foo(x INTEGER);").unwrap();
let mut insert_stmt = db.prepare("INSERT INTO foo(x) VALUES(?)").unwrap(); let mut insert_stmt = db.prepare("INSERT INTO foo(x) VALUES(?)").unwrap();
assert_eq!(insert_stmt.execute(&[1i32]).unwrap(), 1); assert_eq!(insert_stmt.execute([1i32]).unwrap(), 1);
assert_eq!(insert_stmt.execute(&[2i32]).unwrap(), 1); assert_eq!(insert_stmt.execute([2i32]).unwrap(), 1);
assert_eq!(insert_stmt.execute(&[3i32]).unwrap(), 1); assert_eq!(insert_stmt.execute([3i32]).unwrap(), 1);
let mut query = db let mut query = db
.prepare("SELECT x FROM foo WHERE x < ? ORDER BY x DESC") .prepare("SELECT x FROM foo WHERE x < ? ORDER BY x DESC")
.unwrap(); .unwrap();
{ {
let mut rows = query.query(&[4i32]).unwrap(); let mut rows = query.query([4i32]).unwrap();
let mut v = Vec::<i32>::new(); let mut v = Vec::<i32>::new();
while let Some(row) = rows.next().unwrap() { while let Some(row) = rows.next().unwrap() {
@ -1315,7 +1334,7 @@ mod test {
} }
{ {
let mut rows = query.query(&[3i32]).unwrap(); let mut rows = query.query([3i32]).unwrap();
let mut v = Vec::<i32>::new(); let mut v = Vec::<i32>::new();
while let Some(row) = rows.next().unwrap() { while let Some(row) = rows.next().unwrap() {
@ -1339,11 +1358,7 @@ mod test {
db.execute_batch(sql).unwrap(); db.execute_batch(sql).unwrap();
let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap(); let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap();
let results: Result<Vec<String>> = query let results: Result<Vec<String>> = query.query([]).unwrap().map(|row| row.get(1)).collect();
.query(NO_PARAMS)
.unwrap()
.map(|row| row.get(1))
.collect();
assert_eq!(results.unwrap().concat(), "hello, world!"); assert_eq!(results.unwrap().concat(), "hello, world!");
} }
@ -1362,18 +1377,17 @@ mod test {
assert_eq!( assert_eq!(
10i64, 10i64,
db.query_row::<i64, _, _>("SELECT SUM(x) FROM foo", NO_PARAMS, |r| r.get(0)) db.query_row::<i64, _, _>("SELECT SUM(x) FROM foo", [], |r| r.get(0))
.unwrap() .unwrap()
); );
let result: Result<i64> = let result: Result<i64> = db.query_row("SELECT x FROM foo WHERE x > 5", [], |r| r.get(0));
db.query_row("SELECT x FROM foo WHERE x > 5", NO_PARAMS, |r| r.get(0));
match result.unwrap_err() { match result.unwrap_err() {
Error::QueryReturnedNoRows => (), Error::QueryReturnedNoRows => (),
err => panic!("Unexpected error {}", err), err => panic!("Unexpected error {}", err),
} }
let bad_query_result = db.query_row("NOT A PROPER QUERY; test123", NO_PARAMS, |_| Ok(())); let bad_query_result = db.query_row("NOT A PROPER QUERY; test123", [], |_| Ok(()));
assert!(bad_query_result.is_err()); assert!(bad_query_result.is_err());
} }
@ -1382,22 +1396,21 @@ mod test {
fn test_optional() { fn test_optional() {
let db = checked_memory_handle(); let db = checked_memory_handle();
let result: Result<i64> = db.query_row("SELECT 1 WHERE 0 <> 0", NO_PARAMS, |r| r.get(0)); let result: Result<i64> = db.query_row("SELECT 1 WHERE 0 <> 0", [], |r| r.get(0));
let result = result.optional(); let result = result.optional();
match result.unwrap() { match result.unwrap() {
None => (), None => (),
_ => panic!("Unexpected result"), _ => panic!("Unexpected result"),
} }
let result: Result<i64> = db.query_row("SELECT 1 WHERE 0 == 0", NO_PARAMS, |r| r.get(0)); let result: Result<i64> = db.query_row("SELECT 1 WHERE 0 == 0", [], |r| r.get(0));
let result = result.optional(); let result = result.optional();
match result.unwrap() { match result.unwrap() {
Some(1) => (), Some(1) => (),
_ => panic!("Unexpected result"), _ => panic!("Unexpected result"),
} }
let bad_query_result: Result<i64> = let bad_query_result: Result<i64> = db.query_row("NOT A PROPER QUERY", [], |r| r.get(0));
db.query_row("NOT A PROPER QUERY", NO_PARAMS, |r| r.get(0));
let bad_query_result = bad_query_result.optional(); let bad_query_result = bad_query_result.optional();
assert!(bad_query_result.is_err()); assert!(bad_query_result.is_err());
} }
@ -1408,12 +1421,12 @@ mod test {
assert_eq!( assert_eq!(
"memory", "memory",
db.query_row::<String, _, _>("PRAGMA journal_mode", NO_PARAMS, |r| r.get(0)) db.query_row::<String, _, _>("PRAGMA journal_mode", [], |r| r.get(0))
.unwrap() .unwrap()
); );
assert_eq!( assert_eq!(
"off", "off",
db.query_row::<String, _, _>("PRAGMA journal_mode=off", NO_PARAMS, |r| r.get(0)) db.query_row::<String, _, _>("PRAGMA journal_mode=off", [], |r| r.get(0))
.unwrap() .unwrap()
); );
} }
@ -1438,7 +1451,7 @@ mod test {
let mut stmt = db.prepare("INSERT INTO foo DEFAULT VALUES").unwrap(); let mut stmt = db.prepare("INSERT INTO foo DEFAULT VALUES").unwrap();
for _ in 0i32..9 { for _ in 0i32..9 {
stmt.execute(NO_PARAMS).unwrap(); stmt.execute([]).unwrap();
} }
assert_eq!(db.last_insert_rowid(), 10); assert_eq!(db.last_insert_rowid(), 10);
} }
@ -1460,7 +1473,7 @@ mod test {
let mut stmt = db.prepare("PRAGMA schema_version").unwrap(); let mut stmt = db.prepare("PRAGMA schema_version").unwrap();
assert!(!db.is_busy()); assert!(!db.is_busy());
{ {
let mut rows = stmt.query(NO_PARAMS).unwrap(); let mut rows = stmt.query([]).unwrap();
assert!(!db.is_busy()); assert!(!db.is_busy());
let row = rows.next().unwrap(); let row = rows.next().unwrap();
assert!(db.is_busy()); assert!(db.is_busy());
@ -1492,7 +1505,7 @@ mod test {
let db = checked_memory_handle(); let db = checked_memory_handle();
db.execute_batch("CREATE TABLE foo(x NOT NULL)").unwrap(); db.execute_batch("CREATE TABLE foo(x NOT NULL)").unwrap();
let result = db.execute("INSERT INTO foo (x) VALUES (NULL)", NO_PARAMS); let result = db.execute("INSERT INTO foo (x) VALUES (NULL)", []);
assert!(result.is_err()); assert!(result.is_err());
match result.unwrap_err() { match result.unwrap_err() {
@ -1536,7 +1549,7 @@ mod test {
.prepare("SELECT interrupt() FROM (SELECT 1 UNION SELECT 2 UNION SELECT 3)") .prepare("SELECT interrupt() FROM (SELECT 1 UNION SELECT 2 UNION SELECT 3)")
.unwrap(); .unwrap();
let result: Result<Vec<i32>> = stmt.query(NO_PARAMS).unwrap().map(|r| r.get(0)).collect(); let result: Result<Vec<i32>> = stmt.query([]).unwrap().map(|r| r.get(0)).collect();
match result.unwrap_err() { match result.unwrap_err() {
Error::SqliteFailure(err, _) => { Error::SqliteFailure(err, _) => {
@ -1576,7 +1589,7 @@ mod test {
} }
let mut query = db.prepare("SELECT i, x FROM foo").unwrap(); let mut query = db.prepare("SELECT i, x FROM foo").unwrap();
let mut rows = query.query(NO_PARAMS).unwrap(); let mut rows = query.query([]).unwrap();
while let Some(row) = rows.next().unwrap() { while let Some(row) = rows.next().unwrap() {
let i = row.get_raw(0).as_i64().unwrap(); let i = row.get_raw(0).as_i64().unwrap();
@ -1651,7 +1664,7 @@ mod test {
let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap(); let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap();
let results: Result<Vec<String>> = query let results: Result<Vec<String>> = query
.query_and_then(NO_PARAMS, |row| row.get(1)) .query_and_then([], |row| row.get(1))
.unwrap() .unwrap()
.collect(); .collect();
@ -1672,7 +1685,7 @@ mod test {
let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap(); let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap();
let bad_type: Result<Vec<f64>> = query let bad_type: Result<Vec<f64>> = query
.query_and_then(NO_PARAMS, |row| row.get(1)) .query_and_then([], |row| row.get(1))
.unwrap() .unwrap()
.collect(); .collect();
@ -1682,7 +1695,7 @@ mod test {
} }
let bad_idx: Result<Vec<String>> = query let bad_idx: Result<Vec<String>> = query
.query_and_then(NO_PARAMS, |row| row.get(3)) .query_and_then([], |row| row.get(3))
.unwrap() .unwrap()
.collect(); .collect();
@ -1706,7 +1719,7 @@ mod test {
let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap(); let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap();
let results: CustomResult<Vec<String>> = query let results: CustomResult<Vec<String>> = query
.query_and_then(NO_PARAMS, |row| row.get(1).map_err(CustomError::Sqlite)) .query_and_then([], |row| row.get(1).map_err(CustomError::Sqlite))
.unwrap() .unwrap()
.collect(); .collect();
@ -1727,7 +1740,7 @@ mod test {
let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap(); let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap();
let bad_type: CustomResult<Vec<f64>> = query let bad_type: CustomResult<Vec<f64>> = query
.query_and_then(NO_PARAMS, |row| row.get(1).map_err(CustomError::Sqlite)) .query_and_then([], |row| row.get(1).map_err(CustomError::Sqlite))
.unwrap() .unwrap()
.collect(); .collect();
@ -1737,7 +1750,7 @@ mod test {
} }
let bad_idx: CustomResult<Vec<String>> = query let bad_idx: CustomResult<Vec<String>> = query
.query_and_then(NO_PARAMS, |row| row.get(3).map_err(CustomError::Sqlite)) .query_and_then([], |row| row.get(3).map_err(CustomError::Sqlite))
.unwrap() .unwrap()
.collect(); .collect();
@ -1747,7 +1760,7 @@ mod test {
} }
let non_sqlite_err: CustomResult<Vec<String>> = query let non_sqlite_err: CustomResult<Vec<String>> = query
.query_and_then(NO_PARAMS, |_| Err(CustomError::SomeError)) .query_and_then([], |_| Err(CustomError::SomeError))
.unwrap() .unwrap()
.collect(); .collect();
@ -1767,9 +1780,8 @@ mod test {
db.execute_batch(sql).unwrap(); db.execute_batch(sql).unwrap();
let query = "SELECT x, y FROM foo ORDER BY x DESC"; let query = "SELECT x, y FROM foo ORDER BY x DESC";
let results: CustomResult<String> = db.query_row_and_then(query, NO_PARAMS, |row| { let results: CustomResult<String> =
row.get(1).map_err(CustomError::Sqlite) db.query_row_and_then(query, [], |row| row.get(1).map_err(CustomError::Sqlite));
});
assert_eq!(results.unwrap(), "hello"); assert_eq!(results.unwrap(), "hello");
} }
@ -1784,18 +1796,16 @@ mod test {
db.execute_batch(sql).unwrap(); db.execute_batch(sql).unwrap();
let query = "SELECT x, y FROM foo ORDER BY x DESC"; let query = "SELECT x, y FROM foo ORDER BY x DESC";
let bad_type: CustomResult<f64> = db.query_row_and_then(query, NO_PARAMS, |row| { let bad_type: CustomResult<f64> =
row.get(1).map_err(CustomError::Sqlite) db.query_row_and_then(query, [], |row| row.get(1).map_err(CustomError::Sqlite));
});
match bad_type.unwrap_err() { match bad_type.unwrap_err() {
CustomError::Sqlite(Error::InvalidColumnType(..)) => (), CustomError::Sqlite(Error::InvalidColumnType(..)) => (),
err => panic!("Unexpected error {}", err), err => panic!("Unexpected error {}", err),
} }
let bad_idx: CustomResult<String> = db.query_row_and_then(query, NO_PARAMS, |row| { let bad_idx: CustomResult<String> =
row.get(3).map_err(CustomError::Sqlite) db.query_row_and_then(query, [], |row| row.get(3).map_err(CustomError::Sqlite));
});
match bad_idx.unwrap_err() { match bad_idx.unwrap_err() {
CustomError::Sqlite(Error::InvalidColumnIndex(_)) => (), CustomError::Sqlite(Error::InvalidColumnIndex(_)) => (),
@ -1803,7 +1813,7 @@ mod test {
} }
let non_sqlite_err: CustomResult<String> = let non_sqlite_err: CustomResult<String> =
db.query_row_and_then(query, NO_PARAMS, |_| Err(CustomError::SomeError)); db.query_row_and_then(query, [], |_| Err(CustomError::SomeError));
match non_sqlite_err.unwrap_err() { match non_sqlite_err.unwrap_err() {
CustomError::SomeError => (), CustomError::SomeError => (),
@ -1821,7 +1831,7 @@ mod test {
END;"; END;";
db.execute_batch(sql).unwrap(); db.execute_batch(sql).unwrap();
db.query_row("SELECT * FROM foo", NO_PARAMS, |r| { db.query_row("SELECT * FROM foo", [], |r| {
assert_eq!(2, r.column_count()); assert_eq!(2, r.column_count());
Ok(()) Ok(())
}) })
@ -1832,8 +1842,8 @@ mod test {
let db = checked_memory_handle(); let db = checked_memory_handle();
db.execute_batch("CREATE TABLE foo(x INTEGER);").unwrap(); db.execute_batch("CREATE TABLE foo(x INTEGER);").unwrap();
let b: Box<dyn ToSql> = Box::new(5); let b: Box<dyn ToSql> = Box::new(5);
db.execute("INSERT INTO foo VALUES(?)", &[b]).unwrap(); db.execute("INSERT INTO foo VALUES(?)", [b]).unwrap();
db.query_row("SELECT x FROM foo", NO_PARAMS, |r| { db.query_row("SELECT x FROM foo", [], |r| {
assert_eq!(5, r.get_unwrap::<_, i32>(0)); assert_eq!(5, r.get_unwrap::<_, i32>(0));
Ok(()) Ok(())
}) })
@ -1867,7 +1877,7 @@ mod test {
let db = checked_memory_handle(); let db = checked_memory_handle();
db.execute_batch("CREATE TABLE x(t);").unwrap(); db.execute_batch("CREATE TABLE x(t);").unwrap();
// `execute_batch` should be used but `execute` should also work // `execute_batch` should be used but `execute` should also work
db.execute("ALTER TABLE x RENAME TO y;", NO_PARAMS).unwrap(); db.execute("ALTER TABLE x RENAME TO y;", []).unwrap();
} }
#[test] #[test]
@ -1880,7 +1890,7 @@ mod test {
let batch = Batch::new(&db, sql); let batch = Batch::new(&db, sql);
for stmt in batch { for stmt in batch {
let mut stmt = stmt.unwrap(); let mut stmt = stmt.unwrap();
stmt.execute(NO_PARAMS).unwrap(); stmt.execute([]).unwrap();
} }
} }
} }

198
src/params.rs Normal file
View File

@ -0,0 +1,198 @@
use crate::{Result, Statement, ToSql};
mod sealed {
/// This trait exists just to ensure that the only impls of `trait Params`
/// that are allowed are ones in this crate.
pub trait Sealed {}
}
// must not be `pub use`.
use sealed::Sealed;
/// Trait used for parameter sets passed into SQL statements/queries.
///
/// Currently, this trait can only be implemented inside this crate.
pub trait Params: Sealed {
/// Binds the parameters to the statement. It is unlikely calling this
/// explicitly will do what you want. Please use `Statement::query` or
/// similar directly.
// For now, just hide the function in the docs...
#[doc(hidden)]
fn bind_in(self, stmt: &mut Statement<'_>) -> Result<()>;
}
// Explicitly impl for empty array. Critically, for `conn.execute([])` to be
// unambiguous, this must be the *only* implementation for an empty array. This
// avoids `NO_PARAMS` being a necessary part of the API.
impl Sealed for [&dyn ToSql; 0] {}
impl Params for [&dyn ToSql; 0] {
#[inline]
fn bind_in(self, _: &mut Statement<'_>) -> Result<()> {
Ok(())
}
}
impl Sealed for &[&dyn ToSql] {}
impl Params for &[&dyn ToSql] {
#[inline]
fn bind_in(self, stmt: &mut Statement<'_>) -> Result<()> {
stmt.bind_parameters(self)
}
}
impl Sealed for &[(&str, &dyn ToSql)] {}
impl Params for &[(&str, &dyn ToSql)] {
#[inline]
fn bind_in(self, stmt: &mut Statement<'_>) -> Result<()> {
stmt.bind_parameters_named(self)
}
}
macro_rules! impl_for_array_ref {
($($N:literal)+) => {$(
impl<T: ToSql + ?Sized> Sealed for &[&T; $N] {}
impl<T: ToSql + ?Sized> Params for &[&T; $N] {
fn bind_in(self, stmt: &mut Statement<'_>) -> Result<()> {
stmt.bind_parameters(self)
}
}
impl<T: ToSql + ?Sized> Sealed for &[(&str, &T); $N] {}
impl<T: ToSql + ?Sized> Params for &[(&str, &T); $N] {
fn bind_in(self, stmt: &mut Statement<'_>) -> Result<()> {
stmt.bind_parameters_named(self)
}
}
impl<T: ToSql> Sealed for [T; $N] {}
impl<T: ToSql> Params for [T; $N] {
fn bind_in(self, stmt: &mut Statement<'_>) -> Result<()> {
stmt.bind_parameters(&self)
}
}
)+};
}
impl_for_array_ref!(
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
18 19 20 21 22 23 24 25 26 27 29 30 31 32
);
/// Adapter type which allows any iterator over [`ToSql`] values to implement
/// [`Params`].
///
/// This struct is created by the [`params_from_iter`] function.
///
/// This can be useful if you have something like an `&[String]` (of unknown
/// length), and you want to use them with an API that wants something
/// implementing `Params`. This way, you can avoid having to allocate storage
/// for something like a `&[&dyn ToSql]`.
///
/// This essentially is only ever actually needed when dynamically generating
/// SQL — static SQL (by definition) has the number of parameters known
/// statically. As dynamically generating SQL is itself pretty advanced, this
/// API is itself for advanced use cases (See "Realistic use case" in the
/// examples).
///
/// # Example
///
/// ## Basic usage
///
/// ```rust,no_run
/// use rusqlite::{Connection, Result, params_from_iter};
/// use std::collections::BTreeSet;
///
/// fn query(conn: &Connection, ids: &BTreeSet<String>) -> Result<()> {
/// assert_eq!(ids.len(), 3, "Unrealistic sample code");
///
/// let mut stmt = conn.prepare("SELECT * FROM users WHERE id IN (?, ?, ?)")?;
/// let _rows = stmt.query(params_from_iter(ids.iter()))?;
///
/// // use _rows...
/// Ok(())
/// }
/// ```
///
/// ## Realistic use case
///
/// Here's how you'd use `ParamsFromIter` to call a function with no `_iter`
/// equivalent, e.g. [`Statement::exists`].
///
/// ```rust,no_run
/// use rusqlite::{Connection, Result};
///
/// pub fn any_active_users(conn: &Connection, usernames: &[String]) -> Result<bool> {
/// if usernames.is_empty() {
/// return Ok(false);
/// }
///
/// // Note: `repeat_vars` never returns anything attacker-controlled, so
/// // it's fine to use it in a dynamically-built SQL string.
/// let vars = repeat_vars(usernames.len());
///
/// let sql = format!(
/// // In practice this would probably be better as an `EXISTS` query.
/// "SELECT 1 FROM user WHERE is_active AND name IN ({}) LIMIT 1",
/// vars,
/// );
/// let mut stmt = conn.prepare(&sql)?;
/// stmt.exists(rusqlite::params_from_iter(usernames))
/// }
///
/// // Helper function to return a comma-separated sequence of `?`.
/// // - `repeat_vars(0) => panic!(...)`
/// // - `repeat_vars(1) => "?"`
/// // - `repeat_vars(2) => "?,?"`
/// // - `repeat_vars(3) => "?,?,?"`
/// // - ...
/// fn repeat_vars(count: usize) -> String {
/// assert_ne!(count, 0);
/// let mut s = "?,".repeat(count);
/// // Remove trailing comma
/// s.pop();
/// s
/// }
/// ```
///
/// That is fairly complex, and even so would need even more work to be fully
/// production-ready:
///
/// - production code should ensure `usernames` isn't so large that it will
/// surpass [`conn.limit(Limit::SQLITE_LIMIT_VARIABLE_NUMBER)`][limits])
/// (chunking if too large).
///
/// - `repeat_vars` can be implemented in a way that avoids needing to allocate
/// a String.
///
/// [limits]: crate::Connection::limit
///
/// This complexity reflects the fact that `ParamsFromIter` is mainly intended
/// for advanced use cases — most of the time you should know how many
/// parameters you have statically.
#[derive(Clone, Debug)]
pub struct ParamsFromIter<I>(I);
/// Constructor function for a [`ParamsFromIter`]. See its documentation for
/// more.
#[inline]
pub fn params_from_iter<I>(iter: I) -> ParamsFromIter<I>
where
I: IntoIterator,
I::Item: ToSql,
{
ParamsFromIter(iter)
}
impl<I> Sealed for ParamsFromIter<I>
where
I: IntoIterator,
I::Item: ToSql,
{
}
impl<I> Params for ParamsFromIter<I>
where
I: IntoIterator,
I::Item: ToSql,
{
#[inline]
fn bind_in(self, stmt: &mut Statement<'_>) -> Result<()> {
stmt.bind_parameters(self.0)
}
}

View File

@ -427,65 +427,44 @@ mod tests {
let conn = Connection::open_in_memory().expect("failed to create in-memoory database"); let conn = Connection::open_in_memory().expect("failed to create in-memoory database");
conn.execute( conn.execute(
"CREATE TABLE test (a INTEGER)", "CREATE TABLE test (a INTEGER)",
std::iter::empty::<&dyn ToSql>(), crate::params_from_iter(std::iter::empty::<&dyn ToSql>()),
) )
.expect("failed to create table"); .expect("failed to create table");
conn.execute( conn.execute("INSERT INTO test VALUES (42)", [])
"INSERT INTO test VALUES (42)", .expect("failed to insert value");
std::iter::empty::<&dyn ToSql>(),
)
.expect("failed to insert value");
let val = conn let val = conn
.query_row( .query_row("SELECT a FROM test", [], |row| <(u32,)>::try_from(row))
"SELECT a FROM test",
std::iter::empty::<&dyn ToSql>(),
|row| <(u32,)>::try_from(row),
)
.expect("failed to query row"); .expect("failed to query row");
assert_eq!(val, (42,)); assert_eq!(val, (42,));
let fail = conn.query_row( let fail = conn.query_row("SELECT a FROM test", [], |row| <(u32, u32)>::try_from(row));
"SELECT a FROM test",
std::iter::empty::<&dyn ToSql>(),
|row| <(u32, u32)>::try_from(row),
);
assert!(fail.is_err()); assert!(fail.is_err());
} }
#[test] #[test]
fn test_try_from_row_for_tuple_2() { fn test_try_from_row_for_tuple_2() {
use crate::{Connection, ToSql}; use crate::Connection;
use std::convert::TryFrom; use std::convert::TryFrom;
let conn = Connection::open_in_memory().expect("failed to create in-memoory database"); let conn = Connection::open_in_memory().expect("failed to create in-memoory database");
conn.execute( conn.execute("CREATE TABLE test (a INTEGER, b INTEGER)", [])
"CREATE TABLE test (a INTEGER, b INTEGER)", .expect("failed to create table");
std::iter::empty::<&dyn ToSql>(), conn.execute("INSERT INTO test VALUES (42, 47)", [])
) .expect("failed to insert value");
.expect("failed to create table");
conn.execute(
"INSERT INTO test VALUES (42, 47)",
std::iter::empty::<&dyn ToSql>(),
)
.expect("failed to insert value");
let val = conn let val = conn
.query_row( .query_row("SELECT a, b FROM test", [], |row| {
"SELECT a, b FROM test", <(u32, u32)>::try_from(row)
std::iter::empty::<&dyn ToSql>(), })
|row| <(u32, u32)>::try_from(row),
)
.expect("failed to query row"); .expect("failed to query row");
assert_eq!(val, (42, 47)); assert_eq!(val, (42, 47));
let fail = conn.query_row( let fail = conn.query_row("SELECT a, b FROM test", [], |row| {
"SELECT a, b FROM test", <(u32, u32, u32)>::try_from(row)
std::iter::empty::<&dyn ToSql>(), });
|row| <(u32, u32, u32)>::try_from(row),
);
assert!(fail.is_err()); assert!(fail.is_err());
} }
#[test] #[test]
fn test_try_from_row_for_tuple_16() { fn test_try_from_row_for_tuple_16() {
use crate::{Connection, ToSql}; use crate::Connection;
use std::convert::TryFrom; use std::convert::TryFrom;
let create_table = "CREATE TABLE test ( let create_table = "CREATE TABLE test (
@ -546,16 +525,12 @@ mod tests {
); );
let conn = Connection::open_in_memory().expect("failed to create in-memoory database"); let conn = Connection::open_in_memory().expect("failed to create in-memoory database");
conn.execute(create_table, std::iter::empty::<&dyn ToSql>()) conn.execute(create_table, [])
.expect("failed to create table"); .expect("failed to create table");
conn.execute(insert_values, std::iter::empty::<&dyn ToSql>()) conn.execute(insert_values, [])
.expect("failed to insert value"); .expect("failed to insert value");
let val = conn let val = conn
.query_row( .query_row("SELECT * FROM test", [], |row| BigTuple::try_from(row))
"SELECT * FROM test",
std::iter::empty::<&dyn ToSql>(),
|row| BigTuple::try_from(row),
)
.expect("failed to query row"); .expect("failed to query row");
// Debug is not implemented for tuples of 16 // Debug is not implemented for tuples of 16
assert_eq!(val.0, 0); assert_eq!(val.0, 0);

View File

@ -8,7 +8,7 @@ use std::{convert, fmt, mem, ptr, str};
use super::ffi; use super::ffi;
use super::{len_as_c_int, str_for_sqlite}; use super::{len_as_c_int, str_for_sqlite};
use super::{ use super::{
AndThenRows, Connection, Error, MappedRows, RawStatement, Result, Row, Rows, ValueRef, AndThenRows, Connection, Error, MappedRows, Params, RawStatement, Result, Row, Rows, ValueRef,
}; };
use crate::types::{ToSql, ToSqlOutput}; use crate::types::{ToSql, ToSqlOutput};
#[cfg(feature = "array")] #[cfg(feature = "array")]
@ -28,48 +28,28 @@ impl Statement<'_> {
/// ///
/// ## Example /// ## Example
/// ///
/// ### Use with positional parameters
///
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Connection, Result}; /// # use rusqlite::{Connection, Result, params};
/// fn update_rows(conn: &Connection) -> Result<()> { /// fn update_rows(conn: &Connection) -> Result<()> {
/// let mut stmt = conn.prepare("UPDATE foo SET bar = 'baz' WHERE qux = ?")?; /// let mut stmt = conn.prepare("UPDATE foo SET bar = 'baz' WHERE qux = ?")?;
/// ///
/// stmt.execute(&[1i32])?; /// stmt.execute(params![1i32])?;
/// stmt.execute(&[2i32])?; /// // Similarly...
/// stmt.execute(&[&2i32])?;
/// ///
/// Ok(()) /// Ok(())
/// } /// }
/// ``` /// ```
/// ///
/// # Failure /// ### Use with named parameters
///
/// Will return `Err` if binding parameters fails, the executed statement
/// returns rows (in which case `query` should be used instead), or the
/// underlying SQLite call fails.
pub fn execute<P>(&mut self, params: P) -> Result<usize>
where
P: IntoIterator,
P::Item: ToSql,
{
self.bind_parameters(params)?;
self.execute_with_bound_parameters()
}
/// Execute the prepared statement with named parameter(s). If any
/// parameters that were in the prepared statement are not included in
/// `params`, they will continue to use the most-recently bound value
/// from a previous call to `execute_named`, or `NULL` if they have
/// never been bound.
///
/// On success, returns the number of rows that were changed or inserted or
/// deleted (via `sqlite3_changes`).
///
/// ## Example
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Connection, Result}; /// # use rusqlite::{Connection, Result};
/// fn insert(conn: &Connection) -> Result<usize> { /// fn insert(conn: &Connection) -> Result<usize> {
/// let mut stmt = conn.prepare("INSERT INTO test (name) VALUES (:name)")?; /// let mut stmt = conn.prepare("INSERT INTO test (name) VALUES (:name)")?;
/// stmt.execute_named(&[(":name", &"one")]) /// stmt.execute(&[(":name", &"one")])
/// } /// }
/// ``` /// ```
/// ///
@ -80,7 +60,18 @@ impl Statement<'_> {
/// # use rusqlite::{Connection, Result, named_params}; /// # use rusqlite::{Connection, Result, named_params};
/// fn insert(conn: &Connection) -> Result<usize> { /// fn insert(conn: &Connection) -> Result<usize> {
/// let mut stmt = conn.prepare("INSERT INTO test (name) VALUES (:name)")?; /// let mut stmt = conn.prepare("INSERT INTO test (name) VALUES (:name)")?;
/// stmt.execute_named(named_params!{":name": "one"}) /// stmt.execute(named_params!{":name": "one"})
/// }
/// ```
///
/// ### Use without parameters
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result, params};
/// fn delete_all(conn: &Connection) -> Result<()> {
/// let mut stmt = conn.prepare("DELETE FROM users")?;
/// stmt.execute([])?;
/// Ok(())
/// } /// }
/// ``` /// ```
/// ///
@ -89,11 +80,34 @@ impl Statement<'_> {
/// Will return `Err` if binding parameters fails, the executed statement /// Will return `Err` if binding parameters fails, the executed statement
/// returns rows (in which case `query` should be used instead), or the /// returns rows (in which case `query` should be used instead), or the
/// underlying SQLite call fails. /// underlying SQLite call fails.
pub fn execute_named(&mut self, params: &[(&str, &dyn ToSql)]) -> Result<usize> { pub fn execute<P: Params>(&mut self, params: P) -> Result<usize> {
self.bind_parameters_named(params)?; params.bind_in(self)?;
self.execute_with_bound_parameters() self.execute_with_bound_parameters()
} }
/// Execute the prepared statement with named parameter(s).
///
/// Note: This function is deprecated in favor of [`Statement::execute`],
/// which can now take named parameters directly.
///
/// If any parameters that were in the prepared statement are not included
/// in `params`, they will continue to use the most-recently bound value
/// from a previous call to `execute_named`, or `NULL` if they have never
/// been bound.
///
/// On success, returns the number of rows that were changed or inserted or
/// deleted (via `sqlite3_changes`).
///
/// # Failure
///
/// Will return `Err` if binding parameters fails, the executed statement
/// returns rows (in which case `query` should be used instead), or the
/// underlying SQLite call fails.
#[deprecated = "You can use `execute` with named params now."]
pub fn execute_named(&mut self, params: &[(&str, &dyn ToSql)]) -> Result<usize> {
self.execute(params)
}
/// Execute an INSERT and return the ROWID. /// Execute an INSERT and return the ROWID.
/// ///
/// # Note /// # Note
@ -107,11 +121,7 @@ impl Statement<'_> {
/// # Failure /// # Failure
/// ///
/// Will return `Err` if no row is inserted or many rows are inserted. /// Will return `Err` if no row is inserted or many rows are inserted.
pub fn insert<P>(&mut self, params: P) -> Result<i64> pub fn insert<P: Params>(&mut self, params: P) -> Result<i64> {
where
P: IntoIterator,
P::Item: ToSql,
{
let changes = self.execute(params)?; let changes = self.execute(params)?;
match changes { match changes {
1 => Ok(self.conn.last_insert_rowid()), 1 => Ok(self.conn.last_insert_rowid()),
@ -128,11 +138,13 @@ impl Statement<'_> {
/// ///
/// ## Example /// ## Example
/// ///
/// ### Use without parameters
///
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Connection, Result, NO_PARAMS}; /// # use rusqlite::{Connection, Result};
/// fn get_names(conn: &Connection) -> Result<Vec<String>> { /// fn get_names(conn: &Connection) -> Result<Vec<String>> {
/// let mut stmt = conn.prepare("SELECT name FROM people")?; /// let mut stmt = conn.prepare("SELECT name FROM people")?;
/// let mut rows = stmt.query(NO_PARAMS)?; /// let mut rows = stmt.query([])?;
/// ///
/// let mut names = Vec::new(); /// let mut names = Vec::new();
/// while let Some(row) = rows.next()? { /// while let Some(row) = rows.next()? {
@ -143,32 +155,41 @@ impl Statement<'_> {
/// } /// }
/// ``` /// ```
/// ///
/// ## Failure /// ### Use with positional parameters
/// ///
/// Will return `Err` if binding parameters fails. /// ```rust,no_run
pub fn query<P>(&mut self, params: P) -> Result<Rows<'_>> /// # use rusqlite::{Connection, Result};
where /// fn query(conn: &Connection, name: &str) -> Result<()> {
P: IntoIterator, /// let mut stmt = conn.prepare("SELECT * FROM test where name = ?")?;
P::Item: ToSql, /// let mut rows = stmt.query(rusqlite::params![name])?;
{ /// while let Some(row) = rows.next()? {
self.check_readonly()?; /// // ...
self.bind_parameters(params)?; /// }
Ok(Rows::new(self)) /// Ok(())
} /// }
/// ```
/// Execute the prepared statement with named parameter(s), returning a
/// handle for the resulting rows. If any parameters that were in the
/// prepared statement are not included in `params`, they will continue
/// to use the most-recently bound value from a previous
/// call to `query_named`, or `NULL` if they have never been bound.
/// ///
/// ## Example /// Or, equivalently (but without the [`params!`] macro).
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn query(conn: &Connection, name: &str) -> Result<()> {
/// let mut stmt = conn.prepare("SELECT * FROM test where name = ?")?;
/// let mut rows = stmt.query(&[name])?;
/// while let Some(row) = rows.next()? {
/// // ...
/// }
/// Ok(())
/// }
/// ```
///
/// ### Use with named parameters
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Connection, Result}; /// # use rusqlite::{Connection, Result};
/// fn query(conn: &Connection) -> Result<()> { /// fn query(conn: &Connection) -> Result<()> {
/// let mut stmt = conn.prepare("SELECT * FROM test where name = :name")?; /// let mut stmt = conn.prepare("SELECT * FROM test where name = :name")?;
/// let mut rows = stmt.query_named(&[(":name", &"one")])?; /// let mut rows = stmt.query(&[(":name", &"one")])?;
/// while let Some(row) = rows.next()? { /// while let Some(row) = rows.next()? {
/// // ... /// // ...
/// } /// }
@ -183,7 +204,7 @@ impl Statement<'_> {
/// # use rusqlite::{Connection, Result, named_params}; /// # use rusqlite::{Connection, Result, named_params};
/// fn query(conn: &Connection) -> Result<()> { /// fn query(conn: &Connection) -> Result<()> {
/// let mut stmt = conn.prepare("SELECT * FROM test where name = :name")?; /// let mut stmt = conn.prepare("SELECT * FROM test where name = :name")?;
/// let mut rows = stmt.query_named(named_params!{ ":name": "one" })?; /// let mut rows = stmt.query(named_params!{ ":name": "one" })?;
/// while let Some(row) = rows.next()? { /// while let Some(row) = rows.next()? {
/// // ... /// // ...
/// } /// }
@ -191,25 +212,49 @@ impl Statement<'_> {
/// } /// }
/// ``` /// ```
/// ///
/// ## Failure
///
/// Will return `Err` if binding parameters fails.
pub fn query<P: Params>(&mut self, params: P) -> Result<Rows<'_>> {
self.check_readonly()?;
params.bind_in(self)?;
Ok(Rows::new(self))
}
/// Execute the prepared statement with named parameter(s), returning a
/// handle for the resulting rows.
///
/// Note: This function is deprecated in favor of [`Statement::query`],
/// which can now take named parameters directly.
///
/// If any parameters that were in the prepared statement are not included
/// in `params`, they will continue to use the most-recently bound value
/// from a previous call to `query_named`, or `NULL` if they have never been
/// bound.
///
/// # Failure /// # Failure
/// ///
/// Will return `Err` if binding parameters fails. /// Will return `Err` if binding parameters fails.
#[deprecated = "You can use `query` with named params now."]
pub fn query_named(&mut self, params: &[(&str, &dyn ToSql)]) -> Result<Rows<'_>> { pub fn query_named(&mut self, params: &[(&str, &dyn ToSql)]) -> Result<Rows<'_>> {
self.check_readonly()?; self.query(params)
self.bind_parameters_named(params)?;
Ok(Rows::new(self))
} }
/// Executes the prepared statement and maps a function over the resulting /// Executes the prepared statement and maps a function over the resulting
/// rows, returning an iterator over the mapped function results. /// rows, returning an iterator over the mapped function results.
/// ///
/// `f` is used to tranform the _streaming_ iterator into a _standard_
/// iterator.
///
/// ## Example /// ## Example
/// ///
/// ### Use with positional params
///
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Connection, Result, NO_PARAMS}; /// # use rusqlite::{Connection, Result};
/// fn get_names(conn: &Connection) -> Result<Vec<String>> { /// fn get_names(conn: &Connection) -> Result<Vec<String>> {
/// let mut stmt = conn.prepare("SELECT name FROM people")?; /// let mut stmt = conn.prepare("SELECT name FROM people")?;
/// let rows = stmt.query_map(NO_PARAMS, |row| row.get(0))?; /// let rows = stmt.query_map([], |row| row.get(0))?;
/// ///
/// let mut names = Vec::new(); /// let mut names = Vec::new();
/// for name_result in rows { /// for name_result in rows {
@ -219,16 +264,29 @@ impl Statement<'_> {
/// Ok(names) /// Ok(names)
/// } /// }
/// ``` /// ```
/// `f` is used to tranform the _streaming_ iterator into a _standard_
/// iterator.
/// ///
/// ### Use with named params
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn get_names(conn: &Connection) -> Result<Vec<String>> {
/// let mut stmt = conn.prepare("SELECT name FROM people WHERE id = :id")?;
/// let rows = stmt.query_map(&[(":id", &"one")], |row| row.get(0))?;
///
/// let mut names = Vec::new();
/// for name_result in rows {
/// names.push(name_result?);
/// }
///
/// Ok(names)
/// }
/// ```
/// ## Failure /// ## Failure
/// ///
/// Will return `Err` if binding parameters fails. /// Will return `Err` if binding parameters fails.
pub fn query_map<T, P, F>(&mut self, params: P, f: F) -> Result<MappedRows<'_, F>> pub fn query_map<T, P, F>(&mut self, params: P, f: F) -> Result<MappedRows<'_, F>>
where where
P: IntoIterator, P: Params,
P::Item: ToSql,
F: FnMut(&Row<'_>) -> Result<T>, F: FnMut(&Row<'_>) -> Result<T>,
{ {
let rows = self.query(params)?; let rows = self.query(params)?;
@ -237,33 +295,23 @@ impl Statement<'_> {
/// Execute the prepared statement with named parameter(s), returning an /// Execute the prepared statement with named parameter(s), returning an
/// iterator over the result of calling the mapping function over the /// iterator over the result of calling the mapping function over the
/// query's rows. If any parameters that were in the prepared statement /// query's rows.
///
/// Note: This function is deprecated in favor of [`Statement::query_map`],
/// which can now take named parameters directly.
///
/// If any parameters that were in the prepared statement
/// are not included in `params`, they will continue to use the /// are not included in `params`, they will continue to use the
/// most-recently bound value from a previous call to `query_named`, /// most-recently bound value from a previous call to `query_named`,
/// or `NULL` if they have never been bound. /// or `NULL` if they have never been bound.
/// ///
/// ## Example
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn get_names(conn: &Connection) -> Result<Vec<String>> {
/// let mut stmt = conn.prepare("SELECT name FROM people WHERE id = :id")?;
/// let rows = stmt.query_map_named(&[(":id", &"one")], |row| row.get(0))?;
///
/// let mut names = Vec::new();
/// for name_result in rows {
/// names.push(name_result?);
/// }
///
/// Ok(names)
/// }
/// ```
/// `f` is used to tranform the _streaming_ iterator into a _standard_ /// `f` is used to tranform the _streaming_ iterator into a _standard_
/// iterator. /// iterator.
/// ///
/// ## Failure /// ## Failure
/// ///
/// Will return `Err` if binding parameters fails. /// Will return `Err` if binding parameters fails.
#[deprecated = "You can use `query_map` with named params now."]
pub fn query_map_named<T, F>( pub fn query_map_named<T, F>(
&mut self, &mut self,
params: &[(&str, &dyn ToSql)], params: &[(&str, &dyn ToSql)],
@ -272,38 +320,17 @@ impl Statement<'_> {
where where
F: FnMut(&Row<'_>) -> Result<T>, F: FnMut(&Row<'_>) -> Result<T>,
{ {
let rows = self.query_named(params)?; self.query_map(params, f)
Ok(MappedRows::new(rows, f))
} }
/// Executes the prepared statement and maps a function over the resulting /// Executes the prepared statement and maps a function over the resulting
/// rows, where the function returns a `Result` with `Error` type /// rows, where the function returns a `Result` with `Error` type
/// implementing `std::convert::From<Error>` (so errors can be unified). /// implementing `std::convert::From<Error>` (so errors can be unified).
/// ///
/// # Failure
///
/// Will return `Err` if binding parameters fails.
pub fn query_and_then<T, E, P, F>(&mut self, params: P, f: F) -> Result<AndThenRows<'_, F>>
where
P: IntoIterator,
P::Item: ToSql,
E: convert::From<Error>,
F: FnMut(&Row<'_>) -> Result<T, E>,
{
let rows = self.query(params)?;
Ok(AndThenRows::new(rows, f))
}
/// Execute the prepared statement with named parameter(s), returning an
/// iterator over the result of calling the mapping function over the
/// query's rows. If any parameters that were in the prepared statement
/// are not included in
/// `params`, they will
/// continue to use the most-recently bound value from a previous call
/// to `query_named`, or `NULL` if they have never been bound.
///
/// ## Example /// ## Example
/// ///
/// ### Use with named params
///
/// ```rust,no_run /// ```rust,no_run
/// # use rusqlite::{Connection, Result}; /// # use rusqlite::{Connection, Result};
/// struct Person { /// struct Person {
@ -318,7 +345,7 @@ impl Statement<'_> {
/// fn get_names(conn: &Connection) -> Result<Vec<Person>> { /// fn get_names(conn: &Connection) -> Result<Vec<Person>> {
/// let mut stmt = conn.prepare("SELECT name FROM people WHERE id = :id")?; /// let mut stmt = conn.prepare("SELECT name FROM people WHERE id = :id")?;
/// let rows = /// let rows =
/// stmt.query_and_then_named(&[(":id", &"one")], |row| name_to_person(row.get(0)?))?; /// stmt.query_and_then(&[(":id", &"one")], |row| name_to_person(row.get(0)?))?;
/// ///
/// let mut persons = Vec::new(); /// let mut persons = Vec::new();
/// for person_result in rows { /// for person_result in rows {
@ -329,9 +356,52 @@ impl Statement<'_> {
/// } /// }
/// ``` /// ```
/// ///
/// ### Use with positional params
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn get_names(conn: &Connection) -> Result<Vec<String>> {
/// let mut stmt = conn.prepare("SELECT name FROM people WHERE id = ?")?;
/// let rows = stmt.query_and_then(&["one"], |row| row.get::<_, String>(0))?;
///
/// let mut persons = Vec::new();
/// for person_result in rows {
/// persons.push(person_result?);
/// }
///
/// Ok(persons)
/// }
/// ```
///
/// # Failure
///
/// Will return `Err` if binding parameters fails.
pub fn query_and_then<T, E, P, F>(&mut self, params: P, f: F) -> Result<AndThenRows<'_, F>>
where
P: Params,
E: convert::From<Error>,
F: FnMut(&Row<'_>) -> Result<T, E>,
{
let rows = self.query(params)?;
Ok(AndThenRows::new(rows, f))
}
/// Execute the prepared statement with named parameter(s), returning an
/// iterator over the result of calling the mapping function over the
/// query's rows.
///
/// Note: This function is deprecated in favor of [`Statement::query_and_then`],
/// which can now take named parameters directly.
///
/// If any parameters that were in the prepared statement are not included
/// in `params`, they will continue to use the most-recently bound value
/// from a previous call to `query_named`, or `NULL` if they have never been
/// bound.
///
/// ## Failure /// ## Failure
/// ///
/// Will return `Err` if binding parameters fails. /// Will return `Err` if binding parameters fails.
#[deprecated = "You can use `query_and_then` with named params now."]
pub fn query_and_then_named<T, E, F>( pub fn query_and_then_named<T, E, F>(
&mut self, &mut self,
params: &[(&str, &dyn ToSql)], params: &[(&str, &dyn ToSql)],
@ -341,17 +411,12 @@ impl Statement<'_> {
E: convert::From<Error>, E: convert::From<Error>,
F: FnMut(&Row<'_>) -> Result<T, E>, F: FnMut(&Row<'_>) -> Result<T, E>,
{ {
let rows = self.query_named(params)?; self.query_and_then(params, f)
Ok(AndThenRows::new(rows, f))
} }
/// Return `true` if a query in the SQL statement it executes returns one /// Return `true` if a query in the SQL statement it executes returns one
/// or more rows and `false` if the SQL returns an empty set. /// or more rows and `false` if the SQL returns an empty set.
pub fn exists<P>(&mut self, params: P) -> Result<bool> pub fn exists<P: Params>(&mut self, params: P) -> Result<bool> {
where
P: IntoIterator,
P::Item: ToSql,
{
let mut rows = self.query(params)?; let mut rows = self.query(params)?;
let exists = rows.next()?.is_some(); let exists = rows.next()?.is_some();
Ok(exists) Ok(exists)
@ -372,8 +437,7 @@ impl Statement<'_> {
/// Will return `Err` if the underlying SQLite call fails. /// Will return `Err` if the underlying SQLite call fails.
pub fn query_row<T, P, F>(&mut self, params: P, f: F) -> Result<T> pub fn query_row<T, P, F>(&mut self, params: P, f: F) -> Result<T>
where where
P: IntoIterator, P: Params,
P::Item: ToSql,
F: FnOnce(&Row<'_>) -> Result<T>, F: FnOnce(&Row<'_>) -> Result<T>,
{ {
let mut rows = self.query(params)?; let mut rows = self.query(params)?;
@ -384,6 +448,9 @@ impl Statement<'_> {
/// Convenience method to execute a query with named parameter(s) that is /// Convenience method to execute a query with named parameter(s) that is
/// expected to return a single row. /// expected to return a single row.
/// ///
/// Note: This function is deprecated in favor of [`Statement::query_and_then`],
/// which can now take named parameters directly.
///
/// If the query returns more than one row, all rows except the first are /// If the query returns more than one row, all rows except the first are
/// ignored. /// ignored.
/// ///
@ -395,13 +462,12 @@ impl Statement<'_> {
/// ///
/// Will return `Err` if `sql` cannot be converted to a C-compatible string /// Will return `Err` if `sql` cannot be converted to a C-compatible string
/// or if the underlying SQLite call fails. /// or if the underlying SQLite call fails.
#[deprecated = "You can use `query_row` with named params now."]
pub fn query_row_named<T, F>(&mut self, params: &[(&str, &dyn ToSql)], f: F) -> Result<T> pub fn query_row_named<T, F>(&mut self, params: &[(&str, &dyn ToSql)], f: F) -> Result<T>
where where
F: FnOnce(&Row<'_>) -> Result<T>, F: FnOnce(&Row<'_>) -> Result<T>,
{ {
let mut rows = self.query_named(params)?; self.query_row(params, f)
rows.get_expected_row().and_then(|r| f(&r))
} }
/// Consumes the statement. /// Consumes the statement.
@ -439,7 +505,7 @@ impl Statement<'_> {
Ok(self.stmt.bind_parameter_index(name)) Ok(self.stmt.bind_parameter_index(name))
} }
fn bind_parameters<P>(&mut self, params: P) -> Result<()> pub(crate) fn bind_parameters<P>(&mut self, params: P) -> Result<()>
where where
P: IntoIterator, P: IntoIterator,
P::Item: ToSql, P::Item: ToSql,
@ -460,10 +526,14 @@ impl Statement<'_> {
} }
} }
fn bind_parameters_named(&mut self, params: &[(&str, &dyn ToSql)]) -> Result<()> { pub(crate) fn bind_parameters_named<T: ?Sized + ToSql>(
&mut self,
params: &[(&str, &T)],
) -> Result<()> {
for &(name, value) in params { for &(name, value) in params {
if let Some(i) = self.parameter_index(name)? { if let Some(i) = self.parameter_index(name)? {
self.bind_parameter(value, i)?; let ts: &dyn ToSql = &value;
self.bind_parameter(ts, i)?;
} else { } else {
return Err(Error::InvalidParameterName(name.into())); return Err(Error::InvalidParameterName(name.into()));
} }
@ -839,9 +909,10 @@ pub enum StatementStatus {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::types::ToSql; use crate::types::ToSql;
use crate::{Connection, Error, Result, NO_PARAMS}; use crate::{params_from_iter, Connection, Error, Result, NO_PARAMS};
#[test] #[test]
#[allow(deprecated)]
fn test_execute_named() { fn test_execute_named() {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap(); db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap();
@ -852,13 +923,21 @@ mod test {
1 1
); );
assert_eq!( assert_eq!(
db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)]) db.execute("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)])
.unwrap(), .unwrap(),
1 1
); );
assert_eq!(
db.execute(
"INSERT INTO foo(x) VALUES (:x)",
crate::named_params! {":x": 3i32}
)
.unwrap(),
1
);
assert_eq!( assert_eq!(
3i32, 6i32,
db.query_row_named::<i32, _>( db.query_row_named::<i32, _>(
"SELECT SUM(x) FROM foo WHERE x > :x", "SELECT SUM(x) FROM foo WHERE x > :x",
&[(":x", &0i32)], &[(":x", &0i32)],
@ -866,9 +945,19 @@ mod test {
) )
.unwrap() .unwrap()
); );
assert_eq!(
5i32,
db.query_row::<i32, _, _>(
"SELECT SUM(x) FROM foo WHERE x > :x",
&[(":x", &1i32)],
|r| r.get(0)
)
.unwrap()
);
} }
#[test] #[test]
#[allow(deprecated)]
fn test_stmt_execute_named() { fn test_stmt_execute_named() {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \ let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \
@ -888,9 +977,15 @@ mod test {
stmt.query_row_named::<i32, _>(&[(":name", &"one")], |r| r.get(0)) stmt.query_row_named::<i32, _>(&[(":name", &"one")], |r| r.get(0))
.unwrap() .unwrap()
); );
assert_eq!(
1i32,
stmt.query_row::<i32, _, _>(&[(":name", &"one")], |r| r.get(0))
.unwrap()
);
} }
#[test] #[test]
#[allow(deprecated)]
fn test_query_named() { fn test_query_named() {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
let sql = r#" let sql = r#"
@ -902,13 +997,23 @@ mod test {
let mut stmt = db let mut stmt = db
.prepare("SELECT id FROM test where name = :name") .prepare("SELECT id FROM test where name = :name")
.unwrap(); .unwrap();
let mut rows = stmt.query_named(&[(":name", &"one")]).unwrap(); // legacy `_named` api
{
let mut rows = stmt.query_named(&[(":name", &"one")]).unwrap();
let id: Result<i32> = rows.next().unwrap().unwrap().get(0);
assert_eq!(Ok(1), id);
}
let id: Result<i32> = rows.next().unwrap().unwrap().get(0); // plain api
assert_eq!(Ok(1), id); {
let mut rows = stmt.query(&[(":name", &"one")]).unwrap();
let id: Result<i32> = rows.next().unwrap().unwrap().get(0);
assert_eq!(Ok(1), id);
}
} }
#[test] #[test]
#[allow(deprecated)]
fn test_query_map_named() { fn test_query_map_named() {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
let sql = r#" let sql = r#"
@ -920,18 +1025,34 @@ mod test {
let mut stmt = db let mut stmt = db
.prepare("SELECT id FROM test where name = :name") .prepare("SELECT id FROM test where name = :name")
.unwrap(); .unwrap();
let mut rows = stmt // legacy `_named` api
.query_map_named(&[(":name", &"one")], |row| { {
let id: Result<i32> = row.get(0); let mut rows = stmt
id.map(|i| 2 * i) .query_map_named(&[(":name", &"one")], |row| {
}) let id: Result<i32> = row.get(0);
.unwrap(); id.map(|i| 2 * i)
})
.unwrap();
let doubled_id: i32 = rows.next().unwrap().unwrap(); let doubled_id: i32 = rows.next().unwrap().unwrap();
assert_eq!(2, doubled_id); assert_eq!(2, doubled_id);
}
// plain api
{
let mut rows = stmt
.query_map(&[(":name", &"one")], |row| {
let id: Result<i32> = row.get(0);
id.map(|i| 2 * i)
})
.unwrap();
let doubled_id: i32 = rows.next().unwrap().unwrap();
assert_eq!(2, doubled_id);
}
} }
#[test] #[test]
#[allow(deprecated)]
fn test_query_and_then_named() { fn test_query_and_then_named() {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
let sql = r#" let sql = r#"
@ -969,6 +1090,44 @@ mod test {
} }
#[test] #[test]
fn test_query_and_then_by_name() {
let db = Connection::open_in_memory().unwrap();
let sql = r#"
CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER);
INSERT INTO test(id, name) VALUES (1, "one");
INSERT INTO test(id, name) VALUES (2, "one");
"#;
db.execute_batch(sql).unwrap();
let mut stmt = db
.prepare("SELECT id FROM test where name = :name ORDER BY id ASC")
.unwrap();
let mut rows = stmt
.query_and_then(&[(":name", &"one")], |row| {
let id: i32 = row.get(0)?;
if id == 1 {
Ok(id)
} else {
Err(Error::SqliteSingleThreadedMode)
}
})
.unwrap();
// first row should be Ok
let doubled_id: i32 = rows.next().unwrap().unwrap();
assert_eq!(1, doubled_id);
// second row should be Err
#[allow(clippy::match_wild_err_arm)]
match rows.next().unwrap() {
Ok(_) => panic!("invalid Ok"),
Err(Error::SqliteSingleThreadedMode) => (),
Err(_) => panic!("invalid Err"),
}
}
#[test]
#[allow(deprecated)]
fn test_unbound_parameters_are_null() { fn test_unbound_parameters_are_null() {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
let sql = "CREATE TABLE test (x TEXT, y TEXT)"; let sql = "CREATE TABLE test (x TEXT, y TEXT)";
@ -980,9 +1139,7 @@ mod test {
stmt.execute_named(&[(":x", &"one")]).unwrap(); stmt.execute_named(&[(":x", &"one")]).unwrap();
let result: Option<String> = db let result: Option<String> = db
.query_row("SELECT y FROM test WHERE x = 'one'", NO_PARAMS, |row| { .query_row("SELECT y FROM test WHERE x = 'one'", [], |row| row.get(0))
row.get(0)
})
.unwrap(); .unwrap();
assert!(result.is_none()); assert!(result.is_none());
} }
@ -1027,8 +1184,8 @@ mod test {
let mut stmt = db let mut stmt = db
.prepare("INSERT INTO test (x, y) VALUES (:x, :y)") .prepare("INSERT INTO test (x, y) VALUES (:x, :y)")
.unwrap(); .unwrap();
stmt.execute_named(&[(":x", &"one")]).unwrap(); stmt.execute(&[(":x", &"one")]).unwrap();
stmt.execute_named(&[(":y", &"two")]).unwrap(); stmt.execute(&[(":y", &"two")]).unwrap();
let result: String = db let result: String = db
.query_row("SELECT x FROM test WHERE y = 'two'", NO_PARAMS, |row| { .query_row("SELECT x FROM test WHERE y = 'two'", NO_PARAMS, |row| {
@ -1046,16 +1203,16 @@ mod test {
let mut stmt = db let mut stmt = db
.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?)") .prepare("INSERT OR IGNORE INTO foo (x) VALUES (?)")
.unwrap(); .unwrap();
assert_eq!(stmt.insert(&[1i32]).unwrap(), 1); assert_eq!(stmt.insert(&[&1i32]).unwrap(), 1);
assert_eq!(stmt.insert(&[2i32]).unwrap(), 2); assert_eq!(stmt.insert(&[&2i32]).unwrap(), 2);
match stmt.insert(&[1i32]).unwrap_err() { match stmt.insert(&[&1i32]).unwrap_err() {
Error::StatementChangedRows(0) => (), Error::StatementChangedRows(0) => (),
err => panic!("Unexpected error {}", err), err => panic!("Unexpected error {}", err),
} }
let mut multi = db let mut multi = db
.prepare("INSERT INTO foo (x) SELECT 3 UNION ALL SELECT 4") .prepare("INSERT INTO foo (x) SELECT 3 UNION ALL SELECT 4")
.unwrap(); .unwrap();
match multi.insert(NO_PARAMS).unwrap_err() { match multi.insert([]).unwrap_err() {
Error::StatementChangedRows(2) => (), Error::StatementChangedRows(2) => (),
err => panic!("Unexpected error {}", err), err => panic!("Unexpected error {}", err),
} }
@ -1076,14 +1233,14 @@ mod test {
assert_eq!( assert_eq!(
db.prepare("INSERT INTO foo VALUES (10)") db.prepare("INSERT INTO foo VALUES (10)")
.unwrap() .unwrap()
.insert(NO_PARAMS) .insert([])
.unwrap(), .unwrap(),
1 1
); );
assert_eq!( assert_eq!(
db.prepare("INSERT INTO bar VALUES (10)") db.prepare("INSERT INTO bar VALUES (10)")
.unwrap() .unwrap()
.insert(NO_PARAMS) .insert([])
.unwrap(), .unwrap(),
1 1
); );
@ -1099,9 +1256,9 @@ mod test {
END;"; END;";
db.execute_batch(sql).unwrap(); db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT 1 FROM foo WHERE x = ?").unwrap(); let mut stmt = db.prepare("SELECT 1 FROM foo WHERE x = ?").unwrap();
assert!(stmt.exists(&[1i32]).unwrap()); assert!(stmt.exists([1i32]).unwrap());
assert!(stmt.exists(&[2i32]).unwrap()); assert!(stmt.exists(&[&2i32]).unwrap());
assert!(!stmt.exists(&[0i32]).unwrap()); assert!(!stmt.exists([&0i32]).unwrap());
} }
#[test] #[test]
@ -1114,7 +1271,7 @@ mod test {
END;"; END;";
db.execute_batch(sql).unwrap(); db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT y FROM foo WHERE x = ?").unwrap(); let mut stmt = db.prepare("SELECT y FROM foo WHERE x = ?").unwrap();
let y: Result<i64> = stmt.query_row(&[1i32], |r| r.get(0)); let y: Result<i64> = stmt.query_row([1i32], |r| r.get(0));
assert_eq!(3i64, y.unwrap()); assert_eq!(3i64, y.unwrap());
} }
@ -1127,7 +1284,7 @@ mod test {
END;"; END;";
db.execute_batch(sql).unwrap(); db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT y FROM foo").unwrap(); let mut stmt = db.prepare("SELECT y FROM foo").unwrap();
let y: Result<i64> = stmt.query_row(NO_PARAMS, |r| r.get("y")); let y: Result<i64> = stmt.query_row([], |r| r.get("y"));
assert_eq!(3i64, y.unwrap()); assert_eq!(3i64, y.unwrap());
} }
@ -1165,28 +1322,40 @@ mod test {
.unwrap(); .unwrap();
// existing collection: // existing collection:
let data = vec![1, 2, 3]; let data = vec![1, 2, 3];
db.query_row("SELECT ?1, ?2, ?3", &data, |row| row.get::<_, u8>(0)) db.query_row("SELECT ?1, ?2, ?3", params_from_iter(&data), |row| {
.unwrap(); row.get::<_, u8>(0)
db.query_row("SELECT ?1, ?2, ?3", data.as_slice(), |row| { })
.unwrap();
db.query_row(
"SELECT ?1, ?2, ?3",
params_from_iter(data.as_slice()),
|row| row.get::<_, u8>(0),
)
.unwrap();
db.query_row("SELECT ?1, ?2, ?3", params_from_iter(data), |row| {
row.get::<_, u8>(0) row.get::<_, u8>(0)
}) })
.unwrap(); .unwrap();
db.query_row("SELECT ?1, ?2, ?3", data, |row| row.get::<_, u8>(0))
.unwrap();
use std::collections::BTreeSet; use std::collections::BTreeSet;
let data: BTreeSet<String> = ["one", "two", "three"] let data: BTreeSet<String> = ["one", "two", "three"]
.iter() .iter()
.map(|s| (*s).to_string()) .map(|s| (*s).to_string())
.collect(); .collect();
db.query_row("SELECT ?1, ?2, ?3", &data, |row| row.get::<_, String>(0)) db.query_row("SELECT ?1, ?2, ?3", params_from_iter(&data), |row| {
.unwrap(); row.get::<_, String>(0)
})
.unwrap();
let data = [0; 3]; let data = [0; 3];
db.query_row("SELECT ?1, ?2, ?3", &data, |row| row.get::<_, u8>(0)) db.query_row("SELECT ?1, ?2, ?3", params_from_iter(&data), |row| {
.unwrap(); row.get::<_, u8>(0)
db.query_row("SELECT ?1, ?2, ?3", data.iter(), |row| row.get::<_, u8>(0)) })
.unwrap(); .unwrap();
db.query_row("SELECT ?1, ?2, ?3", params_from_iter(data.iter()), |row| {
row.get::<_, u8>(0)
})
.unwrap();
} }
#[test] #[test]
@ -1243,7 +1412,7 @@ mod test {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
let expected = "a\x00b"; let expected = "a\x00b";
let actual: String = db let actual: String = db
.query_row("SELECT ?", &[&expected], |row| row.get(0)) .query_row("SELECT ?", [expected], |row| row.get(0))
.unwrap(); .unwrap();
assert_eq!(expected, actual); assert_eq!(expected, actual);
} }

View File

@ -142,13 +142,13 @@ mod test {
let mut db = Connection::open_in_memory().unwrap(); let mut db = Connection::open_in_memory().unwrap();
db.trace(Some(tracer)); db.trace(Some(tracer));
{ {
let _ = db.query_row("SELECT ?", &[1i32], |_| Ok(())); let _ = db.query_row("SELECT ?", &[&1i32], |_| Ok(()));
let _ = db.query_row("SELECT ?", &["hello"], |_| Ok(())); let _ = db.query_row("SELECT ?", &["hello"], |_| Ok(()));
} }
db.trace(None); db.trace(None);
{ {
let _ = db.query_row("SELECT ?", &[2i32], |_| Ok(())); let _ = db.query_row("SELECT ?", [2i32], |_| Ok(()));
let _ = db.query_row("SELECT ?", &["goodbye"], |_| Ok(())); let _ = db.query_row("SELECT ?", ["goodbye"], |_| Ok(()));
} }
let traced_stmts = TRACED_STMTS.lock().unwrap(); let traced_stmts = TRACED_STMTS.lock().unwrap();

View File

@ -661,7 +661,7 @@ mod test {
} }
fn insert(x: i32, conn: &Connection) { fn insert(x: i32, conn: &Connection) {
conn.execute("INSERT INTO foo VALUES(?)", &[x]).unwrap(); conn.execute("INSERT INTO foo VALUES(?)", [x]).unwrap();
} }
fn assert_current_sum(x: i32, conn: &Connection) { fn assert_current_sum(x: i32, conn: &Connection) {

View File

@ -94,7 +94,7 @@ mod value_ref;
/// # use rusqlite::types::{Null}; /// # use rusqlite::types::{Null};
/// ///
/// fn insert_null(conn: &Connection) -> Result<usize> { /// fn insert_null(conn: &Connection) -> Result<usize> {
/// conn.execute("INSERT INTO people (name) VALUES (?)", &[Null]) /// conn.execute("INSERT INTO people (name) VALUES (?)", [Null])
/// } /// }
/// ``` /// ```
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
@ -188,7 +188,7 @@ mod test {
let db = checked_memory_handle(); let db = checked_memory_handle();
let s = "hello, world!"; let s = "hello, world!";
db.execute("INSERT INTO foo(t) VALUES (?)", &[s.to_owned()]) db.execute("INSERT INTO foo(t) VALUES (?)", [s.to_owned()])
.unwrap(); .unwrap();
let from: String = db let from: String = db
@ -201,7 +201,7 @@ mod test {
fn test_value() { fn test_value() {
let db = checked_memory_handle(); let db = checked_memory_handle();
db.execute("INSERT INTO foo(i) VALUES (?)", &[Value::Integer(10)]) db.execute("INSERT INTO foo(i) VALUES (?)", [Value::Integer(10)])
.unwrap(); .unwrap();
assert_eq!( assert_eq!(

View File

@ -336,7 +336,7 @@ mod test {
(?, 'neg one'), (?, 'neg two'), (?, 'neg one'), (?, 'neg two'),
(?, 'pos one'), (?, 'pos two'), (?, 'pos one'), (?, 'pos two'),
(?, 'min'), (?, 'max')", (?, 'min'), (?, 'max')",
&[0i128, -1i128, -2i128, 1i128, 2i128, i128::MIN, i128::MAX], [0i128, -1i128, -2i128, 1i128, 2i128, i128::MIN, i128::MAX],
) )
.unwrap(); .unwrap();