mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-23 00:39:20 +08:00
Overhaul query API, removing the need for the _named
variants of all functions, and rusqlite::NO_PARAMS
This commit is contained in:
parent
c45446ba73
commit
032aea73b8
@ -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
|
||||
|
@ -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(())
|
||||
/// }
|
||||
|
216
src/lib.rs
216
src/lib.rs
@ -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
198
src/params.rs
Normal 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)
|
||||
}
|
||||
}
|
59
src/row.rs
59
src/row.rs
@ -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);
|
||||
|
475
src/statement.rs
475
src/statement.rs
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -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!(
|
||||
|
@ -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();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user