diff options
Diffstat (limited to 'src/statement.rs')
-rw-r--r-- | src/statement.rs | 317 |
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); |