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).
//! db.execute(
//! "INSERT INTO test_table (content) VALUES (?)",
//! &[ZeroBlob(64)],
//! [ZeroBlob(64)],
//! )?;
//!
//! // given a new row ID, we can reopen the blob on that row
@ -182,7 +182,7 @@
//! // rust (potentially with a dynamic size).
//! db.execute(
//! "INSERT INTO test_table (content) VALUES (?)",
//! &[ZeroBlob(64)],
//! [ZeroBlob(64)],
//! )?;
//!
//! // 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(
//! "SELECT regexp('[aeiou]*', 'aaaaeeeiii')",
//! NO_PARAMS,
//! [],
//! |row| row.get(0),
//! )?;
//!
@ -311,7 +311,7 @@ impl Connection {
/// # Example
///
/// ```rust
/// # use rusqlite::{Connection, Result, NO_PARAMS};
/// # use rusqlite::{Connection, Result};
/// # use rusqlite::functions::FunctionFlags;
/// fn scalar_function_example(db: Connection) -> Result<()> {
/// 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);
/// Ok(())
/// }

View File

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

View File

@ -8,7 +8,7 @@ use std::{convert, fmt, mem, ptr, str};
use super::ffi;
use super::{len_as_c_int, str_for_sqlite};
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};
#[cfg(feature = "array")]
@ -28,48 +28,28 @@ impl Statement<'_> {
///
/// ## Example
///
/// ### Use with positional parameters
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// # use rusqlite::{Connection, Result, params};
/// fn update_rows(conn: &Connection) -> Result<()> {
/// let mut stmt = conn.prepare("UPDATE foo SET bar = 'baz' WHERE qux = ?")?;
///
/// stmt.execute(&[1i32])?;
/// stmt.execute(&[2i32])?;
/// stmt.execute(params![1i32])?;
/// // Similarly...
/// stmt.execute(&[&2i32])?;
///
/// Ok(())
/// }
/// ```
///
/// # 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.
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
/// ### Use with named parameters
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn insert(conn: &Connection) -> Result<usize> {
/// 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};
/// fn insert(conn: &Connection) -> Result<usize> {
/// 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
/// returns rows (in which case `query` should be used instead), or the
/// underlying SQLite call fails.
pub fn execute_named(&mut self, params: &[(&str, &dyn ToSql)]) -> Result<usize> {
self.bind_parameters_named(params)?;
pub fn execute<P: Params>(&mut self, params: P) -> Result<usize> {
params.bind_in(self)?;
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.
///
/// # Note
@ -107,11 +121,7 @@ impl Statement<'_> {
/// # Failure
///
/// Will return `Err` if no row is inserted or many rows are inserted.
pub fn insert<P>(&mut self, params: P) -> Result<i64>
where
P: IntoIterator,
P::Item: ToSql,
{
pub fn insert<P: Params>(&mut self, params: P) -> Result<i64> {
let changes = self.execute(params)?;
match changes {
1 => Ok(self.conn.last_insert_rowid()),
@ -128,11 +138,13 @@ impl Statement<'_> {
///
/// ## Example
///
/// ### Use without parameters
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result, NO_PARAMS};
/// # use rusqlite::{Connection, Result};
/// fn get_names(conn: &Connection) -> Result<Vec<String>> {
/// 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();
/// while let Some(row) = rows.next()? {
@ -143,32 +155,41 @@ impl Statement<'_> {
/// }
/// ```
///
/// ## Failure
/// ### Use with positional parameters
///
/// Will return `Err` if binding parameters fails.
pub fn query<P>(&mut self, params: P) -> Result<Rows<'_>>
where
P: IntoIterator,
P::Item: ToSql,
{
self.check_readonly()?;
self.bind_parameters(params)?;
Ok(Rows::new(self))
}
/// 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.
/// ```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(rusqlite::params![name])?;
/// while let Some(row) = rows.next()? {
/// // ...
/// }
/// Ok(())
/// }
/// ```
///
/// ## 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
/// # use rusqlite::{Connection, Result};
/// fn query(conn: &Connection) -> Result<()> {
/// 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()? {
/// // ...
/// }
@ -183,7 +204,7 @@ impl Statement<'_> {
/// # use rusqlite::{Connection, Result, named_params};
/// fn query(conn: &Connection) -> Result<()> {
/// 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()? {
/// // ...
/// }
@ -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
///
/// 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<'_>> {
self.check_readonly()?;
self.bind_parameters_named(params)?;
Ok(Rows::new(self))
self.query(params)
}
/// Executes the prepared statement and maps a function over the resulting
/// rows, returning an iterator over the mapped function results.
///
/// `f` is used to tranform the _streaming_ iterator into a _standard_
/// iterator.
///
/// ## Example
///
/// ### Use with positional params
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result, NO_PARAMS};
/// # use rusqlite::{Connection, Result};
/// fn get_names(conn: &Connection) -> Result<Vec<String>> {
/// 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();
/// for name_result in rows {
@ -219,16 +264,29 @@ impl Statement<'_> {
/// 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
///
/// Will return `Err` if binding parameters fails.
pub fn query_map<T, P, F>(&mut self, params: P, f: F) -> Result<MappedRows<'_, F>>
where
P: IntoIterator,
P::Item: ToSql,
P: Params,
F: FnMut(&Row<'_>) -> Result<T>,
{
let rows = self.query(params)?;
@ -237,33 +295,23 @@ impl Statement<'_> {
/// 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
/// 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
/// most-recently bound value from a previous call to `query_named`,
/// 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_
/// iterator.
///
/// ## Failure
///
/// Will return `Err` if binding parameters fails.
#[deprecated = "You can use `query_map` with named params now."]
pub fn query_map_named<T, F>(
&mut self,
params: &[(&str, &dyn ToSql)],
@ -272,38 +320,17 @@ impl Statement<'_> {
where
F: FnMut(&Row<'_>) -> Result<T>,
{
let rows = self.query_named(params)?;
Ok(MappedRows::new(rows, f))
self.query_map(params, f)
}
/// Executes the prepared statement and maps a function over the resulting
/// rows, where the function returns a `Result` with `Error` type
/// 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
///
/// ### Use with named params
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// struct Person {
@ -318,7 +345,7 @@ impl Statement<'_> {
/// fn get_names(conn: &Connection) -> Result<Vec<Person>> {
/// let mut stmt = conn.prepare("SELECT name FROM people WHERE id = :id")?;
/// 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();
/// 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
///
/// 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>(
&mut self,
params: &[(&str, &dyn ToSql)],
@ -341,17 +411,12 @@ impl Statement<'_> {
E: convert::From<Error>,
F: FnMut(&Row<'_>) -> Result<T, E>,
{
let rows = self.query_named(params)?;
Ok(AndThenRows::new(rows, f))
self.query_and_then(params, f)
}
/// 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.
pub fn exists<P>(&mut self, params: P) -> Result<bool>
where
P: IntoIterator,
P::Item: ToSql,
{
pub fn exists<P: Params>(&mut self, params: P) -> Result<bool> {
let mut rows = self.query(params)?;
let exists = rows.next()?.is_some();
Ok(exists)
@ -372,8 +437,7 @@ impl Statement<'_> {
/// Will return `Err` if the underlying SQLite call fails.
pub fn query_row<T, P, F>(&mut self, params: P, f: F) -> Result<T>
where
P: IntoIterator,
P::Item: ToSql,
P: Params,
F: FnOnce(&Row<'_>) -> Result<T>,
{
let mut rows = self.query(params)?;
@ -384,6 +448,9 @@ impl Statement<'_> {
/// Convenience method to execute a query with named parameter(s) that is
/// 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
/// ignored.
///
@ -395,13 +462,12 @@ impl Statement<'_> {
///
/// Will return `Err` if `sql` cannot be converted to a C-compatible string
/// or if the underlying SQLite call fails.
#[deprecated = "You can use `query_row` with named params now."]
pub fn query_row_named<T, F>(&mut self, params: &[(&str, &dyn ToSql)], f: F) -> Result<T>
where
F: FnOnce(&Row<'_>) -> Result<T>,
{
let mut rows = self.query_named(params)?;
rows.get_expected_row().and_then(|r| f(&r))
self.query_row(params, f)
}
/// Consumes the statement.
@ -439,7 +505,7 @@ impl Statement<'_> {
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
P: IntoIterator,
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 {
if let Some(i) = self.parameter_index(name)? {
self.bind_parameter(value, i)?;
let ts: &dyn ToSql = &value;
self.bind_parameter(ts, i)?;
} else {
return Err(Error::InvalidParameterName(name.into()));
}
@ -839,9 +909,10 @@ pub enum StatementStatus {
#[cfg(test)]
mod test {
use crate::types::ToSql;
use crate::{Connection, Error, Result, NO_PARAMS};
use crate::{params_from_iter, Connection, Error, Result, NO_PARAMS};
#[test]
#[allow(deprecated)]
fn test_execute_named() {
let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap();
@ -852,13 +923,21 @@ mod test {
1
);
assert_eq!(
db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)])
db.execute("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)])
.unwrap(),
1
);
assert_eq!(
db.execute(
"INSERT INTO foo(x) VALUES (:x)",
crate::named_params! {":x": 3i32}
)
.unwrap(),
1
);
assert_eq!(
3i32,
6i32,
db.query_row_named::<i32, _>(
"SELECT SUM(x) FROM foo WHERE x > :x",
&[(":x", &0i32)],
@ -866,9 +945,19 @@ mod test {
)
.unwrap()
);
assert_eq!(
5i32,
db.query_row::<i32, _, _>(
"SELECT SUM(x) FROM foo WHERE x > :x",
&[(":x", &1i32)],
|r| r.get(0)
)
.unwrap()
);
}
#[test]
#[allow(deprecated)]
fn test_stmt_execute_named() {
let db = Connection::open_in_memory().unwrap();
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))
.unwrap()
);
assert_eq!(
1i32,
stmt.query_row::<i32, _, _>(&[(":name", &"one")], |r| r.get(0))
.unwrap()
);
}
#[test]
#[allow(deprecated)]
fn test_query_named() {
let db = Connection::open_in_memory().unwrap();
let sql = r#"
@ -902,13 +997,23 @@ mod test {
let mut stmt = db
.prepare("SELECT id FROM test where name = :name")
.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);
}
// plain api
{
let mut rows = stmt.query(&[(":name", &"one")]).unwrap();
let id: Result<i32> = rows.next().unwrap().unwrap().get(0);
assert_eq!(Ok(1), id);
}
}
#[test]
#[allow(deprecated)]
fn test_query_map_named() {
let db = Connection::open_in_memory().unwrap();
let sql = r#"
@ -920,6 +1025,8 @@ mod test {
let mut stmt = db
.prepare("SELECT id FROM test where name = :name")
.unwrap();
// legacy `_named` api
{
let mut rows = stmt
.query_map_named(&[(":name", &"one")], |row| {
let id: Result<i32> = row.get(0);
@ -930,8 +1037,22 @@ mod test {
let doubled_id: i32 = rows.next().unwrap().unwrap();
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]
#[allow(deprecated)]
fn test_query_and_then_named() {
let db = Connection::open_in_memory().unwrap();
let sql = r#"
@ -969,6 +1090,44 @@ mod 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() {
let db = Connection::open_in_memory().unwrap();
let sql = "CREATE TABLE test (x TEXT, y TEXT)";
@ -980,9 +1139,7 @@ mod test {
stmt.execute_named(&[(":x", &"one")]).unwrap();
let result: Option<String> = db
.query_row("SELECT y FROM test WHERE x = 'one'", NO_PARAMS, |row| {
row.get(0)
})
.query_row("SELECT y FROM test WHERE x = 'one'", [], |row| row.get(0))
.unwrap();
assert!(result.is_none());
}
@ -1027,8 +1184,8 @@ mod test {
let mut stmt = db
.prepare("INSERT INTO test (x, y) VALUES (:x, :y)")
.unwrap();
stmt.execute_named(&[(":x", &"one")]).unwrap();
stmt.execute_named(&[(":y", &"two")]).unwrap();
stmt.execute(&[(":x", &"one")]).unwrap();
stmt.execute(&[(":y", &"two")]).unwrap();
let result: String = db
.query_row("SELECT x FROM test WHERE y = 'two'", NO_PARAMS, |row| {
@ -1046,16 +1203,16 @@ mod test {
let mut stmt = db
.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?)")
.unwrap();
assert_eq!(stmt.insert(&[1i32]).unwrap(), 1);
assert_eq!(stmt.insert(&[2i32]).unwrap(), 2);
match stmt.insert(&[1i32]).unwrap_err() {
assert_eq!(stmt.insert(&[&1i32]).unwrap(), 1);
assert_eq!(stmt.insert(&[&2i32]).unwrap(), 2);
match stmt.insert(&[&1i32]).unwrap_err() {
Error::StatementChangedRows(0) => (),
err => panic!("Unexpected error {}", err),
}
let mut multi = db
.prepare("INSERT INTO foo (x) SELECT 3 UNION ALL SELECT 4")
.unwrap();
match multi.insert(NO_PARAMS).unwrap_err() {
match multi.insert([]).unwrap_err() {
Error::StatementChangedRows(2) => (),
err => panic!("Unexpected error {}", err),
}
@ -1076,14 +1233,14 @@ mod test {
assert_eq!(
db.prepare("INSERT INTO foo VALUES (10)")
.unwrap()
.insert(NO_PARAMS)
.insert([])
.unwrap(),
1
);
assert_eq!(
db.prepare("INSERT INTO bar VALUES (10)")
.unwrap()
.insert(NO_PARAMS)
.insert([])
.unwrap(),
1
);
@ -1099,9 +1256,9 @@ mod test {
END;";
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT 1 FROM foo WHERE x = ?").unwrap();
assert!(stmt.exists(&[1i32]).unwrap());
assert!(stmt.exists(&[2i32]).unwrap());
assert!(!stmt.exists(&[0i32]).unwrap());
assert!(stmt.exists([1i32]).unwrap());
assert!(stmt.exists(&[&2i32]).unwrap());
assert!(!stmt.exists([&0i32]).unwrap());
}
#[test]
@ -1114,7 +1271,7 @@ mod test {
END;";
db.execute_batch(sql).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());
}
@ -1127,7 +1284,7 @@ mod test {
END;";
db.execute_batch(sql).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());
}
@ -1165,13 +1322,19 @@ mod test {
.unwrap();
// existing collection:
let data = vec![1, 2, 3];
db.query_row("SELECT ?1, ?2, ?3", &data, |row| row.get::<_, u8>(0))
.unwrap();
db.query_row("SELECT ?1, ?2, ?3", data.as_slice(), |row| {
db.query_row("SELECT ?1, ?2, ?3", params_from_iter(&data), |row| {
row.get::<_, u8>(0)
})
.unwrap();
db.query_row("SELECT ?1, ?2, ?3", data, |row| row.get::<_, u8>(0))
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)
})
.unwrap();
use std::collections::BTreeSet;
@ -1179,13 +1342,19 @@ mod test {
.iter()
.map(|s| (*s).to_string())
.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| {
row.get::<_, String>(0)
})
.unwrap();
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| {
row.get::<_, u8>(0)
})
.unwrap();
db.query_row("SELECT ?1, ?2, ?3", data.iter(), |row| row.get::<_, u8>(0))
db.query_row("SELECT ?1, ?2, ?3", params_from_iter(data.iter()), |row| {
row.get::<_, u8>(0)
})
.unwrap();
}
@ -1243,7 +1412,7 @@ mod test {
let db = Connection::open_in_memory().unwrap();
let expected = "a\x00b";
let actual: String = db
.query_row("SELECT ?", &[&expected], |row| row.get(0))
.query_row("SELECT ?", [expected], |row| row.get(0))
.unwrap();
assert_eq!(expected, actual);
}

View File

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

View File

@ -661,7 +661,7 @@ mod test {
}
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) {

View File

@ -94,7 +94,7 @@ mod value_ref;
/// # use rusqlite::types::{Null};
///
/// 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)]
@ -188,7 +188,7 @@ mod test {
let db = checked_memory_handle();
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();
let from: String = db
@ -201,7 +201,7 @@ mod test {
fn test_value() {
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();
assert_eq!(

View File

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