aboutsummaryrefslogtreecommitdiff
path: root/src/statement.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/statement.rs')
-rw-r--r--src/statement.rs317
1 files changed, 59 insertions, 258 deletions
diff --git a/src/statement.rs b/src/statement.rs
index ee5e220..982567a 100644
--- a/src/statement.rs
+++ b/src/statement.rs
@@ -33,7 +33,7 @@ impl Statement<'_> {
/// ```rust,no_run
/// # use rusqlite::{Connection, Result, params};
/// fn update_rows(conn: &Connection) -> Result<()> {
- /// let mut stmt = conn.prepare("UPDATE foo SET bar = ? WHERE qux = ?")?;
+ /// let mut stmt = conn.prepare("UPDATE foo SET bar = ?1 WHERE qux = ?2")?;
/// // For a single parameter, or a parameter where all the values have
/// // the same type, just passing an array is simplest.
/// stmt.execute([2i32])?;
@@ -58,7 +58,7 @@ impl Statement<'_> {
/// fn store_file(conn: &Connection, path: &str, data: &[u8]) -> Result<()> {
/// # // no need to do it for real.
/// # fn sha256(_: &[u8]) -> [u8; 32] { [0; 32] }
- /// let query = "INSERT OR REPLACE INTO files(path, hash, data) VALUES (?, ?, ?)";
+ /// let query = "INSERT OR REPLACE INTO files(path, hash, data) VALUES (?1, ?2, ?3)";
/// let mut stmt = conn.prepare_cached(query)?;
/// let hash: [u8; 32] = sha256(data);
/// // The easiest way to pass positional parameters of have several
@@ -114,31 +114,6 @@ impl Statement<'_> {
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.
- #[doc(hidden)]
- #[deprecated = "You can use `execute` with named params now."]
- #[inline]
- pub fn execute_named(&mut self, params: &[(&str, &dyn ToSql)]) -> Result<usize> {
- self.execute(params)
- }
-
/// Execute an INSERT and return the ROWID.
///
/// # Note
@@ -193,7 +168,7 @@ impl Statement<'_> {
/// ```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 stmt = conn.prepare("SELECT * FROM test where name = ?1")?;
/// let mut rows = stmt.query(rusqlite::params![name])?;
/// while let Some(row) = rows.next()? {
/// // ...
@@ -202,12 +177,12 @@ impl Statement<'_> {
/// }
/// ```
///
- /// Or, equivalently (but without the [`params!`] macro).
+ /// Or, equivalently (but without the [`crate::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 stmt = conn.prepare("SELECT * FROM test where name = ?1")?;
/// let mut rows = stmt.query([name])?;
/// while let Some(row) = rows.next()? {
/// // ...
@@ -254,26 +229,6 @@ impl Statement<'_> {
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.
- #[doc(hidden)]
- #[deprecated = "You can use `query` with named params now."]
- pub fn query_named(&mut self, params: &[(&str, &dyn ToSql)]) -> Result<Rows<'_>> {
- self.query(params)
- }
-
/// Executes the prepared statement and maps a function over the resulting
/// rows, returning an iterator over the mapped function results.
///
@@ -328,37 +283,6 @@ impl Statement<'_> {
self.query(params).map(|rows| rows.mapped(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_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.
- ///
- /// `f` is used to transform the _streaming_ iterator into a _standard_
- /// iterator.
- ///
- /// ## Failure
- ///
- /// Will return `Err` if binding parameters fails.
- #[doc(hidden)]
- #[deprecated = "You can use `query_map` with named params now."]
- pub fn query_map_named<T, F>(
- &mut self,
- params: &[(&str, &dyn ToSql)],
- f: F,
- ) -> Result<MappedRows<'_, F>>
- where
- F: FnMut(&Row<'_>) -> Result<T>,
- {
- 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).
@@ -398,7 +322,7 @@ impl Statement<'_> {
/// ```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 mut stmt = conn.prepare("SELECT name FROM people WHERE id = ?1")?;
/// let rows = stmt.query_and_then(["one"], |row| row.get::<_, String>(0))?;
///
/// let mut persons = Vec::new();
@@ -423,36 +347,6 @@ impl Statement<'_> {
self.query(params).map(|rows| rows.and_then(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.
- #[doc(hidden)]
- #[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)],
- f: F,
- ) -> Result<AndThenRows<'_, F>>
- where
- E: From<Error>,
- F: FnMut(&Row<'_>) -> Result<T, E>,
- {
- 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.
#[inline]
@@ -487,35 +381,6 @@ impl Statement<'_> {
rows.get_expected_row().and_then(f)
}
- /// 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.
- ///
- /// Returns `Err(QueryReturnedNoRows)` if no results are returned. If the
- /// query truly is optional, you can call
- /// [`.optional()`](crate::OptionalExtension::optional) on the result of
- /// this to get a `Result<Option<T>>` (requires that the trait
- /// `rusqlite::OptionalExtension` is imported).
- ///
- /// # Failure
- ///
- /// Will return `Err` if `sql` cannot be converted to a C-compatible string
- /// or if the underlying SQLite call fails.
- #[doc(hidden)]
- #[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>,
- {
- self.query_row(params, f)
- }
-
/// Consumes the statement.
///
/// Functionally equivalent to the `Drop` implementation, but allows
@@ -796,7 +661,7 @@ impl Statement<'_> {
self.conn.decode_result(stmt.finalize())
}
- #[cfg(all(feature = "modern_sqlite", feature = "extra_check"))]
+ #[cfg(feature = "extra_check")]
#[inline]
fn check_update(&self) -> Result<()> {
// sqlite3_column_count works for DML but not for DDL (ie ALTER)
@@ -806,16 +671,6 @@ impl Statement<'_> {
Ok(())
}
- #[cfg(all(not(feature = "modern_sqlite"), feature = "extra_check"))]
- #[inline]
- fn check_update(&self) -> Result<()> {
- // sqlite3_column_count works for DML but not for DDL (ie ALTER)
- if self.column_count() > 0 {
- return Err(Error::ExecuteReturnedResults);
- }
- Ok(())
- }
-
#[cfg(not(feature = "extra_check"))]
#[inline]
#[allow(clippy::unnecessary_wraps)]
@@ -825,8 +680,6 @@ impl Statement<'_> {
/// Returns a string containing the SQL text of prepared statement with
/// bound parameters expanded.
- #[cfg(feature = "modern_sqlite")]
- #[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
pub fn expanded_sql(&self) -> Option<String> {
self.stmt
.expanded_sql()
@@ -856,6 +709,12 @@ impl Statement<'_> {
self.stmt.is_explain()
}
+ /// Returns true if the statement is read only.
+ #[inline]
+ pub fn readonly(&self) -> bool {
+ self.stmt.readonly()
+ }
+
#[cfg(feature = "extra_check")]
#[inline]
pub(crate) fn check_no_tail(&self) -> Result<()> {
@@ -882,6 +741,11 @@ impl Statement<'_> {
mem::swap(&mut stmt, &mut self.stmt);
stmt
}
+
+ /// Reset all bindings
+ pub fn clear_bindings(&mut self) {
+ self.stmt.clear_bindings()
+ }
}
impl fmt::Debug for Statement<'_> {
@@ -1021,13 +885,12 @@ mod test {
use crate::{params_from_iter, Connection, Error, Result};
#[test]
- #[allow(deprecated)]
fn test_execute_named() -> Result<()> {
let db = Connection::open_in_memory()?;
db.execute_batch("CREATE TABLE foo(x INTEGER)")?;
assert_eq!(
- db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)])?,
+ db.execute("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)])?,
1
);
assert_eq!(
@@ -1044,7 +907,7 @@ mod test {
assert_eq!(
6i32,
- db.query_row_named::<i32, _>(
+ db.query_row::<i32, _, _>(
"SELECT SUM(x) FROM foo WHERE x > :x",
&[(":x", &0i32)],
|r| r.get(0)
@@ -1062,7 +925,6 @@ mod test {
}
#[test]
- #[allow(deprecated)]
fn test_stmt_execute_named() -> Result<()> {
let db = Connection::open_in_memory()?;
let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \
@@ -1070,22 +932,17 @@ mod test {
db.execute_batch(sql)?;
let mut stmt = db.prepare("INSERT INTO test (name) VALUES (:name)")?;
- stmt.execute_named(&[(":name", &"one")])?;
+ stmt.execute(&[(":name", &"one")])?;
let mut stmt = db.prepare("SELECT COUNT(*) FROM test WHERE name = :name")?;
assert_eq!(
1i32,
- stmt.query_row_named::<i32, _>(&[(":name", &"one")], |r| r.get(0))?
- );
- assert_eq!(
- 1i32,
stmt.query_row::<i32, _, _>(&[(":name", "one")], |r| r.get(0))?
);
Ok(())
}
#[test]
- #[allow(deprecated)]
fn test_query_named() -> Result<()> {
let db = Connection::open_in_memory()?;
let sql = r#"
@@ -1095,24 +952,13 @@ mod test {
db.execute_batch(sql)?;
let mut stmt = db.prepare("SELECT id FROM test where name = :name")?;
- // legacy `_named` api
- {
- let mut rows = stmt.query_named(&[(":name", &"one")])?;
- let id: Result<i32> = rows.next()?.unwrap().get(0);
- assert_eq!(Ok(1), id);
- }
-
- // plain api
- {
- let mut rows = stmt.query(&[(":name", "one")])?;
- let id: Result<i32> = rows.next()?.unwrap().get(0);
- assert_eq!(Ok(1), id);
- }
+ let mut rows = stmt.query(&[(":name", "one")])?;
+ let id: Result<i32> = rows.next()?.unwrap().get(0);
+ assert_eq!(Ok(1), id);
Ok(())
}
#[test]
- #[allow(deprecated)]
fn test_query_map_named() -> Result<()> {
let db = Connection::open_in_memory()?;
let sql = r#"
@@ -1122,61 +968,13 @@ mod test {
db.execute_batch(sql)?;
let mut stmt = db.prepare("SELECT id FROM test where name = :name")?;
- // legacy `_named` api
- {
- let mut rows = stmt.query_map_named(&[(":name", &"one")], |row| {
- let id: Result<i32> = row.get(0);
- id.map(|i| 2 * i)
- })?;
-
- let doubled_id: i32 = rows.next().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)
- })?;
-
- let doubled_id: i32 = rows.next().unwrap()?;
- assert_eq!(2, doubled_id);
- }
- Ok(())
- }
-
- #[test]
- #[allow(deprecated)]
- fn test_query_and_then_named() -> Result<()> {
- let db = Connection::open_in_memory()?;
- 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)?;
-
- let mut stmt = db.prepare("SELECT id FROM test where name = :name ORDER BY id ASC")?;
- let mut rows = stmt.query_and_then_named(&[(":name", &"one")], |row| {
- let id: i32 = row.get(0)?;
- if id == 1 {
- Ok(id)
- } else {
- Err(Error::SqliteSingleThreadedMode)
- }
+ let mut rows = stmt.query_map(&[(":name", "one")], |row| {
+ let id: Result<i32> = row.get(0);
+ id.map(|i| 2 * i)
})?;
- // first row should be Ok
let doubled_id: i32 = rows.next().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"),
- }
+ assert_eq!(2, doubled_id);
Ok(())
}
@@ -1215,17 +1013,15 @@ mod test {
}
#[test]
- #[allow(deprecated)]
fn test_unbound_parameters_are_null() -> Result<()> {
let db = Connection::open_in_memory()?;
let sql = "CREATE TABLE test (x TEXT, y TEXT)";
db.execute_batch(sql)?;
let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)")?;
- stmt.execute_named(&[(":x", &"one")])?;
+ stmt.execute(&[(":x", &"one")])?;
- let result: Option<String> =
- db.query_row("SELECT y FROM test WHERE x = 'one'", [], |row| row.get(0))?;
+ let result: Option<String> = db.one_column("SELECT y FROM test WHERE x = 'one'")?;
assert!(result.is_none());
Ok(())
}
@@ -1271,8 +1067,7 @@ mod test {
stmt.execute(&[(":x", "one")])?;
stmt.execute(&[(":y", "two")])?;
- let result: String =
- db.query_row("SELECT x FROM test WHERE y = 'two'", [], |row| row.get(0))?;
+ let result: String = db.one_column("SELECT x FROM test WHERE y = 'two'")?;
assert_eq!(result, "one");
Ok(())
}
@@ -1281,7 +1076,7 @@ mod test {
fn test_insert() -> Result<()> {
let db = Connection::open_in_memory()?;
db.execute_batch("CREATE TABLE foo(x INTEGER UNIQUE)")?;
- let mut stmt = db.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?)")?;
+ let mut stmt = db.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?1)")?;
assert_eq!(stmt.insert([1i32])?, 1);
assert_eq!(stmt.insert([2i32])?, 2);
match stmt.insert([1i32]).unwrap_err() {
@@ -1321,7 +1116,7 @@ mod test {
INSERT INTO foo VALUES(2);
END;";
db.execute_batch(sql)?;
- let mut stmt = db.prepare("SELECT 1 FROM foo WHERE x = ?")?;
+ let mut stmt = db.prepare("SELECT 1 FROM foo WHERE x = ?1")?;
assert!(stmt.exists([1i32])?);
assert!(stmt.exists([2i32])?);
assert!(!stmt.exists([0i32])?);
@@ -1330,18 +1125,18 @@ mod test {
#[test]
fn test_tuple_params() -> Result<()> {
let db = Connection::open_in_memory()?;
- let s = db.query_row("SELECT printf('[%s]', ?)", ("abc",), |r| {
+ let s = db.query_row("SELECT printf('[%s]', ?1)", ("abc",), |r| {
r.get::<_, String>(0)
})?;
assert_eq!(s, "[abc]");
let s = db.query_row(
- "SELECT printf('%d %s %d', ?, ?, ?)",
+ "SELECT printf('%d %s %d', ?1, ?2, ?3)",
(1i32, "abc", 2i32),
|r| r.get::<_, String>(0),
)?;
assert_eq!(s, "1 abc 2");
let s = db.query_row(
- "SELECT printf('%d %s %d %d', ?, ?, ?, ?)",
+ "SELECT printf('%d %s %d %d', ?1, ?2, ?3, ?4)",
(1, "abc", 2i32, 4i64),
|r| r.get::<_, String>(0),
)?;
@@ -1353,10 +1148,10 @@ mod test {
);
let query = "SELECT printf(
'%d %s | %d %s | %d %s | %d %s || %d %s | %d %s | %d %s | %d %s',
- ?, ?, ?, ?,
- ?, ?, ?, ?,
- ?, ?, ?, ?,
- ?, ?, ?, ?
+ ?1, ?2, ?3, ?4,
+ ?5, ?6, ?7, ?8,
+ ?9, ?10, ?11, ?12,
+ ?13, ?14, ?15, ?16
)";
let s = db.query_row(query, bigtup, |r| r.get::<_, String>(0))?;
assert_eq!(s, "0 a | 1 b | 2 c | 3 d || 4 e | 5 f | 6 g | 7 h");
@@ -1372,7 +1167,7 @@ mod test {
INSERT INTO foo VALUES(2, 4);
END;";
db.execute_batch(sql)?;
- let mut stmt = db.prepare("SELECT y FROM foo WHERE x = ?")?;
+ let mut stmt = db.prepare("SELECT y FROM foo WHERE x = ?1")?;
let y: Result<i64> = stmt.query_row([1i32], |r| r.get(0));
assert_eq!(3i64, y?);
Ok(())
@@ -1407,10 +1202,9 @@ mod test {
}
#[test]
- #[cfg(feature = "modern_sqlite")]
fn test_expanded_sql() -> Result<()> {
let db = Connection::open_in_memory()?;
- let stmt = db.prepare("SELECT ?")?;
+ let stmt = db.prepare("SELECT ?1")?;
stmt.bind_parameter(&1, 1)?;
assert_eq!(Some("SELECT 1".to_owned()), stmt.expanded_sql());
Ok(())
@@ -1422,7 +1216,7 @@ mod test {
// dynamic slice:
db.query_row(
"SELECT ?1, ?2, ?3",
- &[&1u8 as &dyn ToSql, &"one", &Some("one")],
+ [&1u8 as &dyn ToSql, &"one", &Some("one")],
|row| row.get::<_, u8>(0),
)?;
// existing collection:
@@ -1474,10 +1268,10 @@ mod test {
let conn = Connection::open_in_memory()?;
let mut stmt = conn.prepare("")?;
assert_eq!(0, stmt.column_count());
- assert!(stmt.parameter_index("test").is_ok());
- assert!(stmt.step().is_err());
+ stmt.parameter_index("test").unwrap();
+ stmt.step().unwrap_err();
stmt.reset();
- assert!(stmt.execute([]).is_err());
+ stmt.execute([]).unwrap_err();
Ok(())
}
@@ -1507,13 +1301,13 @@ mod test {
#[test]
fn test_utf16_conversion() -> Result<()> {
let db = Connection::open_in_memory()?;
- db.pragma_update(None, "encoding", &"UTF-16le")?;
+ db.pragma_update(None, "encoding", "UTF-16le")?;
let encoding: String = db.pragma_query_value(None, "encoding", |row| row.get(0))?;
assert_eq!("UTF-16le", encoding);
db.execute_batch("CREATE TABLE foo(x TEXT)")?;
let expected = "ใƒ†ใ‚นใƒˆ";
- db.execute("INSERT INTO foo(x) VALUES (?)", &[&expected])?;
- let actual: String = db.query_row("SELECT x FROM foo", [], |row| row.get(0))?;
+ db.execute("INSERT INTO foo(x) VALUES (?1)", [&expected])?;
+ let actual: String = db.one_column("SELECT x FROM foo")?;
assert_eq!(expected, actual);
Ok(())
}
@@ -1522,7 +1316,7 @@ mod test {
fn test_nul_byte() -> Result<()> {
let db = Connection::open_in_memory()?;
let expected = "a\x00b";
- let actual: String = db.query_row("SELECT ?", [expected], |row| row.get(0))?;
+ let actual: String = db.query_row("SELECT ?1", [expected], |row| row.get(0))?;
assert_eq!(expected, actual);
Ok(())
}
@@ -1537,12 +1331,19 @@ mod test {
}
#[test]
- #[cfg(all(feature = "modern_sqlite", not(feature = "bundled-sqlcipher")))] // SQLite >= 3.38.0
+ fn readonly() -> Result<()> {
+ let db = Connection::open_in_memory()?;
+ let stmt = db.prepare("SELECT 1;")?;
+ assert!(stmt.readonly());
+ Ok(())
+ }
+
+ #[test]
+ #[cfg(feature = "modern_sqlite")] // SQLite >= 3.38.0
fn test_error_offset() -> Result<()> {
use crate::ffi::ErrorCode;
let db = Connection::open_in_memory()?;
let r = db.execute_batch("SELECT CURRENT_TIMESTANP;");
- assert!(r.is_err());
match r.unwrap_err() {
Error::SqlInputError { error, offset, .. } => {
assert_eq!(error.code, ErrorCode::Unknown);