| use std::iter::IntoIterator; |
| use std::os::raw::{c_int, c_void}; |
| #[cfg(feature = "array")] |
| use std::rc::Rc; |
| use std::slice::from_raw_parts; |
| use std::{fmt, mem, ptr, str}; |
| |
| use super::ffi; |
| use super::{len_as_c_int, str_for_sqlite}; |
| use super::{ |
| AndThenRows, Connection, Error, MappedRows, Params, RawStatement, Result, Row, Rows, ValueRef, |
| }; |
| use crate::types::{ToSql, ToSqlOutput}; |
| #[cfg(feature = "array")] |
| use crate::vtab::array::{free_array, ARRAY_TYPE}; |
| |
| /// A prepared statement. |
| pub struct Statement<'conn> { |
| conn: &'conn Connection, |
| pub(crate) stmt: RawStatement, |
| } |
| |
| impl Statement<'_> { |
| /// Execute the prepared statement. |
| /// |
| /// On success, returns the number of rows that were changed or inserted or |
| /// deleted (via `sqlite3_changes`). |
| /// |
| /// ## Example |
| /// |
| /// ### Use with positional parameters |
| /// |
| /// ```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 = ?")?; |
| /// // For a single parameter, or a parameter where all the values have |
| /// // the same type, just passing an array is simplest. |
| /// stmt.execute([2i32])?; |
| /// // The `rusqlite::params!` macro is mostly useful when the parameters do not |
| /// // all have the same type, or if there are more than 32 parameters |
| /// // at once, but it can be used in other cases. |
| /// stmt.execute(params![1i32])?; |
| /// // However, it's not required, many cases are fine as: |
| /// stmt.execute(&[&2i32])?; |
| /// // Or even: |
| /// stmt.execute([2i32])?; |
| /// // If you really want to, this is an option as well. |
| /// stmt.execute((2i32,))?; |
| /// Ok(()) |
| /// } |
| /// ``` |
| /// |
| /// #### Heterogeneous positional parameters |
| /// |
| /// ``` |
| /// use rusqlite::{Connection, Result}; |
| /// 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 mut stmt = conn.prepare_cached(query)?; |
| /// let hash: [u8; 32] = sha256(data); |
| /// // The easiest way to pass positional parameters of have several |
| /// // different types is by using a tuple. |
| /// stmt.execute((path, hash, data))?; |
| /// // Using the `params!` macro also works, and supports longer parameter lists: |
| /// stmt.execute(rusqlite::params![path, hash, data])?; |
| /// Ok(()) |
| /// } |
| /// # let c = Connection::open_in_memory().unwrap(); |
| /// # c.execute_batch("CREATE TABLE files(path TEXT PRIMARY KEY, hash BLOB, data BLOB)").unwrap(); |
| /// # store_file(&c, "foo/bar.txt", b"bibble").unwrap(); |
| /// # store_file(&c, "foo/baz.txt", b"bobble").unwrap(); |
| /// ``` |
| /// |
| /// ### Use with named parameters |
| /// |
| /// ```rust,no_run |
| /// # use rusqlite::{Connection, Result, named_params}; |
| /// fn insert(conn: &Connection) -> Result<()> { |
| /// let mut stmt = conn.prepare("INSERT INTO test (key, value) VALUES (:key, :value)")?; |
| /// // The `rusqlite::named_params!` macro (like `params!`) is useful for heterogeneous |
| /// // sets of parameters (where all parameters are not the same type), or for queries |
| /// // with many (more than 32) statically known parameters. |
| /// stmt.execute(named_params! { ":key": "one", ":val": 2 })?; |
| /// // However, named parameters can also be passed like: |
| /// stmt.execute(&[(":key", "three"), (":val", "four")])?; |
| /// // Or even: (note that a &T is required for the value type, currently) |
| /// stmt.execute(&[(":key", &100), (":val", &200)])?; |
| /// Ok(()) |
| /// } |
| /// ``` |
| /// |
| /// ### 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(()) |
| /// } |
| /// ``` |
| /// |
| /// # 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. |
| #[inline] |
| 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. |
| #[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 |
| /// |
| /// This function is a convenience wrapper around |
| /// [`execute()`](Statement::execute) intended for queries that insert a |
| /// single item. It is possible to misuse this function in a way that it |
| /// cannot detect, such as by calling it on a statement which _updates_ |
| /// a single item rather than inserting one. Please don't do that. |
| /// |
| /// # Failure |
| /// |
| /// Will return `Err` if no row is inserted or many rows are inserted. |
| #[inline] |
| 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()), |
| _ => Err(Error::StatementChangedRows(changes)), |
| } |
| } |
| |
| /// Execute the prepared statement, returning a handle to the resulting |
| /// rows. |
| /// |
| /// Due to lifetime restrictions, the rows handle returned by `query` does |
| /// not implement the `Iterator` trait. Consider using |
| /// [`query_map`](Statement::query_map) or |
| /// [`query_and_then`](Statement::query_and_then) instead, which do. |
| /// |
| /// ## Example |
| /// |
| /// ### Use without parameters |
| /// |
| /// ```rust,no_run |
| /// # 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([])?; |
| /// |
| /// let mut names = Vec::new(); |
| /// while let Some(row) = rows.next()? { |
| /// names.push(row.get(0)?); |
| /// } |
| /// |
| /// Ok(names) |
| /// } |
| /// ``` |
| /// |
| /// ### Use with positional parameters |
| /// |
| /// ```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(()) |
| /// } |
| /// ``` |
| /// |
| /// 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(&[(":name", "one")])?; |
| /// while let Some(row) = rows.next()? { |
| /// // ... |
| /// } |
| /// Ok(()) |
| /// } |
| /// ``` |
| /// |
| /// Note, the `named_params!` macro is provided for syntactic convenience, |
| /// and so the above example could also be written as: |
| /// |
| /// ```rust,no_run |
| /// # 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_params! { ":name": "one" })?; |
| /// while let Some(row) = rows.next()? { |
| /// // ... |
| /// } |
| /// Ok(()) |
| /// } |
| /// ``` |
| /// |
| /// ## Failure |
| /// |
| /// Will return `Err` if binding parameters fails. |
| #[inline] |
| pub fn query<P: Params>(&mut self, params: P) -> Result<Rows<'_>> { |
| 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. |
| #[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. |
| /// |
| /// `f` is used to transform the _streaming_ iterator into a _standard_ |
| /// iterator. |
| /// |
| /// This is equivalent to `stmt.query(params)?.mapped(f)`. |
| /// |
| /// ## Example |
| /// |
| /// ### 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")?; |
| /// let rows = stmt.query_map([], |row| row.get(0))?; |
| /// |
| /// let mut names = Vec::new(); |
| /// for name_result in rows { |
| /// names.push(name_result?); |
| /// } |
| /// |
| /// Ok(names) |
| /// } |
| /// ``` |
| /// |
| /// ### 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: Params, |
| F: FnMut(&Row<'_>) -> Result<T>, |
| { |
| 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). |
| /// |
| /// This is equivalent to `stmt.query(params)?.and_then(f)`. |
| /// |
| /// ## Example |
| /// |
| /// ### Use with named params |
| /// |
| /// ```rust,no_run |
| /// # use rusqlite::{Connection, Result}; |
| /// struct Person { |
| /// name: String, |
| /// }; |
| /// |
| /// fn name_to_person(name: String) -> Result<Person> { |
| /// // ... check for valid name |
| /// Ok(Person { name }) |
| /// } |
| /// |
| /// 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(&[(":id", "one")], |row| name_to_person(row.get(0)?))?; |
| /// |
| /// let mut persons = Vec::new(); |
| /// for person_result in rows { |
| /// persons.push(person_result?); |
| /// } |
| /// |
| /// Ok(persons) |
| /// } |
| /// ``` |
| /// |
| /// ### 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. |
| #[inline] |
| pub fn query_and_then<T, E, P, F>(&mut self, params: P, f: F) -> Result<AndThenRows<'_, F>> |
| where |
| P: Params, |
| E: From<Error>, |
| F: FnMut(&Row<'_>) -> Result<T, E>, |
| { |
| 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] |
| 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) |
| } |
| |
| /// Convenience method to execute a query that is expected to return a |
| /// single row. |
| /// |
| /// 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 the underlying SQLite call fails. |
| pub fn query_row<T, P, F>(&mut self, params: P, f: F) -> Result<T> |
| where |
| P: Params, |
| F: FnOnce(&Row<'_>) -> Result<T>, |
| { |
| let mut rows = self.query(params)?; |
| |
| 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 |
| /// callers to see any errors that occur. |
| /// |
| /// # Failure |
| /// |
| /// Will return `Err` if the underlying SQLite call fails. |
| #[inline] |
| pub fn finalize(mut self) -> Result<()> { |
| self.finalize_() |
| } |
| |
| /// Return the (one-based) index of an SQL parameter given its name. |
| /// |
| /// Note that the initial ":" or "$" or "@" or "?" used to specify the |
| /// parameter is included as part of the name. |
| /// |
| /// ```rust,no_run |
| /// # use rusqlite::{Connection, Result}; |
| /// fn example(conn: &Connection) -> Result<()> { |
| /// let stmt = conn.prepare("SELECT * FROM test WHERE name = :example")?; |
| /// let index = stmt.parameter_index(":example")?; |
| /// assert_eq!(index, Some(1)); |
| /// Ok(()) |
| /// } |
| /// ``` |
| /// |
| /// # Failure |
| /// |
| /// Will return Err if `name` is invalid. Will return Ok(None) if the name |
| /// is valid but not a bound parameter of this statement. |
| #[inline] |
| pub fn parameter_index(&self, name: &str) -> Result<Option<usize>> { |
| Ok(self.stmt.bind_parameter_index(name)) |
| } |
| |
| /// Return the SQL parameter name given its (one-based) index (the inverse |
| /// of [`Statement::parameter_index`]). |
| /// |
| /// ```rust,no_run |
| /// # use rusqlite::{Connection, Result}; |
| /// fn example(conn: &Connection) -> Result<()> { |
| /// let stmt = conn.prepare("SELECT * FROM test WHERE name = :example")?; |
| /// let index = stmt.parameter_name(1); |
| /// assert_eq!(index, Some(":example")); |
| /// Ok(()) |
| /// } |
| /// ``` |
| /// |
| /// # Failure |
| /// |
| /// Will return `None` if the column index is out of bounds or if the |
| /// parameter is positional. |
| #[inline] |
| pub fn parameter_name(&self, index: usize) -> Option<&'_ str> { |
| self.stmt.bind_parameter_name(index as i32).map(|name| { |
| str::from_utf8(name.to_bytes()).expect("Invalid UTF-8 sequence in parameter name") |
| }) |
| } |
| |
| #[inline] |
| pub(crate) fn bind_parameters<P>(&mut self, params: P) -> Result<()> |
| where |
| P: IntoIterator, |
| P::Item: ToSql, |
| { |
| let expected = self.stmt.bind_parameter_count(); |
| let mut index = 0; |
| for p in params.into_iter() { |
| index += 1; // The leftmost SQL parameter has an index of 1. |
| if index > expected { |
| break; |
| } |
| self.bind_parameter(&p, index)?; |
| } |
| if index != expected { |
| Err(Error::InvalidParameterCount(index, expected)) |
| } else { |
| Ok(()) |
| } |
| } |
| |
| #[inline] |
| pub(crate) fn ensure_parameter_count(&self, n: usize) -> Result<()> { |
| let count = self.parameter_count(); |
| if count != n { |
| Err(Error::InvalidParameterCount(n, count)) |
| } else { |
| Ok(()) |
| } |
| } |
| |
| #[inline] |
| 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)? { |
| let ts: &dyn ToSql = &value; |
| self.bind_parameter(ts, i)?; |
| } else { |
| return Err(Error::InvalidParameterName(name.into())); |
| } |
| } |
| Ok(()) |
| } |
| |
| /// Return the number of parameters that can be bound to this statement. |
| #[inline] |
| pub fn parameter_count(&self) -> usize { |
| self.stmt.bind_parameter_count() |
| } |
| |
| /// Low level API to directly bind a parameter to a given index. |
| /// |
| /// Note that the index is one-based, that is, the first parameter index is |
| /// 1 and not 0. This is consistent with the SQLite API and the values given |
| /// to parameters bound as `?NNN`. |
| /// |
| /// The valid values for `one_based_col_index` begin at `1`, and end at |
| /// [`Statement::parameter_count`], inclusive. |
| /// |
| /// # Caveats |
| /// |
| /// This should not generally be used, but is available for special cases |
| /// such as: |
| /// |
| /// - binding parameters where a gap exists. |
| /// - binding named and positional parameters in the same query. |
| /// - separating parameter binding from query execution. |
| /// |
| /// In general, statements that have had *any* parameters bound this way |
| /// should have *all* parameters bound this way, and be queried or executed |
| /// by [`Statement::raw_query`] or [`Statement::raw_execute`], other usage |
| /// is unsupported and will likely, probably in surprising ways. |
| /// |
| /// That is: Do not mix the "raw" statement functions with the rest of the |
| /// API, or the results may be surprising, and may even change in future |
| /// versions without comment. |
| /// |
| /// # Example |
| /// |
| /// ```rust,no_run |
| /// # use rusqlite::{Connection, Result}; |
| /// fn query(conn: &Connection) -> Result<()> { |
| /// let mut stmt = conn.prepare("SELECT * FROM test WHERE name = :name AND value > ?2")?; |
| /// let name_index = stmt.parameter_index(":name")?.expect("No such parameter"); |
| /// stmt.raw_bind_parameter(name_index, "foo")?; |
| /// stmt.raw_bind_parameter(2, 100)?; |
| /// let mut rows = stmt.raw_query(); |
| /// while let Some(row) = rows.next()? { |
| /// // ... |
| /// } |
| /// Ok(()) |
| /// } |
| /// ``` |
| #[inline] |
| pub fn raw_bind_parameter<T: ToSql>( |
| &mut self, |
| one_based_col_index: usize, |
| param: T, |
| ) -> Result<()> { |
| // This is the same as `bind_parameter` but slightly more ergonomic and |
| // correctly takes `&mut self`. |
| self.bind_parameter(¶m, one_based_col_index) |
| } |
| |
| /// Low level API to execute a statement given that all parameters were |
| /// bound explicitly with the [`Statement::raw_bind_parameter`] API. |
| /// |
| /// # Caveats |
| /// |
| /// Any unbound parameters will have `NULL` as their value. |
| /// |
| /// This should not generally be used outside of special cases, and |
| /// functions in the [`Statement::execute`] family should be preferred. |
| /// |
| /// # Failure |
| /// |
| /// Will return `Err` if the executed statement returns rows (in which case |
| /// `query` should be used instead), or the underlying SQLite call fails. |
| #[inline] |
| pub fn raw_execute(&mut self) -> Result<usize> { |
| self.execute_with_bound_parameters() |
| } |
| |
| /// Low level API to get `Rows` for this query given that all parameters |
| /// were bound explicitly with the [`Statement::raw_bind_parameter`] API. |
| /// |
| /// # Caveats |
| /// |
| /// Any unbound parameters will have `NULL` as their value. |
| /// |
| /// This should not generally be used outside of special cases, and |
| /// functions in the [`Statement::query`] family should be preferred. |
| /// |
| /// Note that if the SQL does not return results, [`Statement::raw_execute`] |
| /// should be used instead. |
| #[inline] |
| pub fn raw_query(&mut self) -> Rows<'_> { |
| Rows::new(self) |
| } |
| |
| // generic because many of these branches can constant fold away. |
| fn bind_parameter<P: ?Sized + ToSql>(&self, param: &P, col: usize) -> Result<()> { |
| let value = param.to_sql()?; |
| |
| let ptr = unsafe { self.stmt.ptr() }; |
| let value = match value { |
| ToSqlOutput::Borrowed(v) => v, |
| ToSqlOutput::Owned(ref v) => ValueRef::from(v), |
| |
| #[cfg(feature = "blob")] |
| ToSqlOutput::ZeroBlob(len) => { |
| // TODO sqlite3_bind_zeroblob64 // 3.8.11 |
| return self |
| .conn |
| .decode_result(unsafe { ffi::sqlite3_bind_zeroblob(ptr, col as c_int, len) }); |
| } |
| #[cfg(feature = "array")] |
| ToSqlOutput::Array(a) => { |
| return self.conn.decode_result(unsafe { |
| ffi::sqlite3_bind_pointer( |
| ptr, |
| col as c_int, |
| Rc::into_raw(a) as *mut c_void, |
| ARRAY_TYPE, |
| Some(free_array), |
| ) |
| }); |
| } |
| }; |
| self.conn.decode_result(match value { |
| ValueRef::Null => unsafe { ffi::sqlite3_bind_null(ptr, col as c_int) }, |
| ValueRef::Integer(i) => unsafe { ffi::sqlite3_bind_int64(ptr, col as c_int, i) }, |
| ValueRef::Real(r) => unsafe { ffi::sqlite3_bind_double(ptr, col as c_int, r) }, |
| ValueRef::Text(s) => unsafe { |
| let (c_str, len, destructor) = str_for_sqlite(s)?; |
| // TODO sqlite3_bind_text64 // 3.8.7 |
| ffi::sqlite3_bind_text(ptr, col as c_int, c_str, len, destructor) |
| }, |
| ValueRef::Blob(b) => unsafe { |
| let length = len_as_c_int(b.len())?; |
| if length == 0 { |
| ffi::sqlite3_bind_zeroblob(ptr, col as c_int, 0) |
| } else { |
| // TODO sqlite3_bind_blob64 // 3.8.7 |
| ffi::sqlite3_bind_blob( |
| ptr, |
| col as c_int, |
| b.as_ptr().cast::<c_void>(), |
| length, |
| ffi::SQLITE_TRANSIENT(), |
| ) |
| } |
| }, |
| }) |
| } |
| |
| #[inline] |
| fn execute_with_bound_parameters(&mut self) -> Result<usize> { |
| self.check_update()?; |
| let r = self.stmt.step(); |
| self.stmt.reset(); |
| match r { |
| ffi::SQLITE_DONE => Ok(self.conn.changes() as usize), |
| ffi::SQLITE_ROW => Err(Error::ExecuteReturnedResults), |
| _ => Err(self.conn.decode_result(r).unwrap_err()), |
| } |
| } |
| |
| #[inline] |
| fn finalize_(&mut self) -> Result<()> { |
| let mut stmt = unsafe { RawStatement::new(ptr::null_mut(), 0) }; |
| mem::swap(&mut stmt, &mut self.stmt); |
| self.conn.decode_result(stmt.finalize()) |
| } |
| |
| #[cfg(all(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 && self.stmt.readonly() { |
| return Err(Error::ExecuteReturnedResults); |
| } |
| 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)] |
| fn check_update(&self) -> Result<()> { |
| Ok(()) |
| } |
| |
| /// 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() |
| .map(|s| s.to_string_lossy().to_string()) |
| } |
| |
| /// Get the value for one of the status counters for this statement. |
| #[inline] |
| pub fn get_status(&self, status: StatementStatus) -> i32 { |
| self.stmt.get_status(status, false) |
| } |
| |
| /// Reset the value of one of the status counters for this statement, |
| #[inline] |
| /// returning the value it had before resetting. |
| pub fn reset_status(&self, status: StatementStatus) -> i32 { |
| self.stmt.get_status(status, true) |
| } |
| |
| /// Returns 1 if the prepared statement is an EXPLAIN statement, |
| /// or 2 if the statement is an EXPLAIN QUERY PLAN, |
| /// or 0 if it is an ordinary statement or a NULL pointer. |
| #[inline] |
| #[cfg(feature = "modern_sqlite")] // 3.28.0 |
| #[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))] |
| pub fn is_explain(&self) -> i32 { |
| self.stmt.is_explain() |
| } |
| |
| #[cfg(feature = "extra_check")] |
| #[inline] |
| pub(crate) fn check_no_tail(&self) -> Result<()> { |
| if self.stmt.has_tail() { |
| Err(Error::MultipleStatement) |
| } else { |
| Ok(()) |
| } |
| } |
| |
| #[cfg(not(feature = "extra_check"))] |
| #[inline] |
| #[allow(clippy::unnecessary_wraps)] |
| pub(crate) fn check_no_tail(&self) -> Result<()> { |
| Ok(()) |
| } |
| |
| /// Safety: This is unsafe, because using `sqlite3_stmt` after the |
| /// connection has closed is illegal, but `RawStatement` does not enforce |
| /// this, as it loses our protective `'conn` lifetime bound. |
| #[inline] |
| pub(crate) unsafe fn into_raw(mut self) -> RawStatement { |
| let mut stmt = RawStatement::new(ptr::null_mut(), 0); |
| mem::swap(&mut stmt, &mut self.stmt); |
| stmt |
| } |
| } |
| |
| impl fmt::Debug for Statement<'_> { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| let sql = if self.stmt.is_null() { |
| Ok("") |
| } else { |
| str::from_utf8(self.stmt.sql().unwrap().to_bytes()) |
| }; |
| f.debug_struct("Statement") |
| .field("conn", self.conn) |
| .field("stmt", &self.stmt) |
| .field("sql", &sql) |
| .finish() |
| } |
| } |
| |
| impl Drop for Statement<'_> { |
| #[allow(unused_must_use)] |
| #[inline] |
| fn drop(&mut self) { |
| self.finalize_(); |
| } |
| } |
| |
| impl Statement<'_> { |
| #[inline] |
| pub(super) fn new(conn: &Connection, stmt: RawStatement) -> Statement<'_> { |
| Statement { conn, stmt } |
| } |
| |
| pub(super) fn value_ref(&self, col: usize) -> ValueRef<'_> { |
| let raw = unsafe { self.stmt.ptr() }; |
| |
| match self.stmt.column_type(col) { |
| ffi::SQLITE_NULL => ValueRef::Null, |
| ffi::SQLITE_INTEGER => { |
| ValueRef::Integer(unsafe { ffi::sqlite3_column_int64(raw, col as c_int) }) |
| } |
| ffi::SQLITE_FLOAT => { |
| ValueRef::Real(unsafe { ffi::sqlite3_column_double(raw, col as c_int) }) |
| } |
| ffi::SQLITE_TEXT => { |
| let s = unsafe { |
| // Quoting from "Using SQLite" book: |
| // To avoid problems, an application should first extract the desired type using |
| // a sqlite3_column_xxx() function, and then call the |
| // appropriate sqlite3_column_bytes() function. |
| let text = ffi::sqlite3_column_text(raw, col as c_int); |
| let len = ffi::sqlite3_column_bytes(raw, col as c_int); |
| assert!( |
| !text.is_null(), |
| "unexpected SQLITE_TEXT column type with NULL data" |
| ); |
| from_raw_parts(text.cast::<u8>(), len as usize) |
| }; |
| |
| ValueRef::Text(s) |
| } |
| ffi::SQLITE_BLOB => { |
| let (blob, len) = unsafe { |
| ( |
| ffi::sqlite3_column_blob(raw, col as c_int), |
| ffi::sqlite3_column_bytes(raw, col as c_int), |
| ) |
| }; |
| |
| assert!( |
| len >= 0, |
| "unexpected negative return from sqlite3_column_bytes" |
| ); |
| if len > 0 { |
| assert!( |
| !blob.is_null(), |
| "unexpected SQLITE_BLOB column type with NULL data" |
| ); |
| ValueRef::Blob(unsafe { from_raw_parts(blob.cast::<u8>(), len as usize) }) |
| } else { |
| // The return value from sqlite3_column_blob() for a zero-length BLOB |
| // is a NULL pointer. |
| ValueRef::Blob(&[]) |
| } |
| } |
| _ => unreachable!("sqlite3_column_type returned invalid value"), |
| } |
| } |
| |
| #[inline] |
| pub(super) fn step(&self) -> Result<bool> { |
| match self.stmt.step() { |
| ffi::SQLITE_ROW => Ok(true), |
| ffi::SQLITE_DONE => Ok(false), |
| code => Err(self.conn.decode_result(code).unwrap_err()), |
| } |
| } |
| |
| #[inline] |
| pub(super) fn reset(&self) -> c_int { |
| self.stmt.reset() |
| } |
| } |
| |
| /// Prepared statement status counters. |
| /// |
| /// See `https://www.sqlite.org/c3ref/c_stmtstatus_counter.html` |
| /// for explanations of each. |
| /// |
| /// Note that depending on your version of SQLite, all of these |
| /// may not be available. |
| #[repr(i32)] |
| #[derive(Clone, Copy, PartialEq, Eq)] |
| #[non_exhaustive] |
| pub enum StatementStatus { |
| /// Equivalent to SQLITE_STMTSTATUS_FULLSCAN_STEP |
| FullscanStep = 1, |
| /// Equivalent to SQLITE_STMTSTATUS_SORT |
| Sort = 2, |
| /// Equivalent to SQLITE_STMTSTATUS_AUTOINDEX |
| AutoIndex = 3, |
| /// Equivalent to SQLITE_STMTSTATUS_VM_STEP |
| VmStep = 4, |
| /// Equivalent to SQLITE_STMTSTATUS_REPREPARE (3.20.0) |
| RePrepare = 5, |
| /// Equivalent to SQLITE_STMTSTATUS_RUN (3.20.0) |
| Run = 6, |
| /// Equivalent to SQLITE_STMTSTATUS_FILTER_MISS |
| FilterMiss = 7, |
| /// Equivalent to SQLITE_STMTSTATUS_FILTER_HIT |
| FilterHit = 8, |
| /// Equivalent to SQLITE_STMTSTATUS_MEMUSED (3.20.0) |
| MemUsed = 99, |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use crate::types::ToSql; |
| 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)])?, |
| 1 |
| ); |
| assert_eq!( |
| db.execute("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)])?, |
| 1 |
| ); |
| assert_eq!( |
| db.execute( |
| "INSERT INTO foo(x) VALUES (:x)", |
| crate::named_params! {":x": 3i32} |
| )?, |
| 1 |
| ); |
| |
| assert_eq!( |
| 6i32, |
| db.query_row_named::<i32, _>( |
| "SELECT SUM(x) FROM foo WHERE x > :x", |
| &[(":x", &0i32)], |
| |r| r.get(0) |
| )? |
| ); |
| assert_eq!( |
| 5i32, |
| db.query_row::<i32, _, _>( |
| "SELECT SUM(x) FROM foo WHERE x > :x", |
| &[(":x", &1i32)], |
| |r| r.get(0) |
| )? |
| ); |
| Ok(()) |
| } |
| |
| #[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 \ |
| INTEGER)"; |
| db.execute_batch(sql)?; |
| |
| let mut stmt = db.prepare("INSERT INTO test (name) VALUES (:name)")?; |
| stmt.execute_named(&[(":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#" |
| CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER); |
| INSERT INTO test(id, name) VALUES (1, "one"); |
| "#; |
| 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); |
| } |
| Ok(()) |
| } |
| |
| #[test] |
| #[allow(deprecated)] |
| fn test_query_map_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"); |
| "#; |
| 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) |
| } |
| })?; |
| |
| // 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"), |
| } |
| Ok(()) |
| } |
| |
| #[test] |
| fn test_query_and_then_by_name() -> 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(&[(":name", "one")], |row| { |
| let id: i32 = row.get(0)?; |
| if id == 1 { |
| Ok(id) |
| } else { |
| Err(Error::SqliteSingleThreadedMode) |
| } |
| })?; |
| |
| // 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"), |
| } |
| Ok(()) |
| } |
| |
| #[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")])?; |
| |
| let result: Option<String> = |
| db.query_row("SELECT y FROM test WHERE x = 'one'", [], |row| row.get(0))?; |
| assert!(result.is_none()); |
| Ok(()) |
| } |
| |
| #[test] |
| fn test_raw_binding() -> Result<()> { |
| let db = Connection::open_in_memory()?; |
| db.execute_batch("CREATE TABLE test (name TEXT, value INTEGER)")?; |
| { |
| let mut stmt = db.prepare("INSERT INTO test (name, value) VALUES (:name, ?3)")?; |
| |
| let name_idx = stmt.parameter_index(":name")?.unwrap(); |
| stmt.raw_bind_parameter(name_idx, "example")?; |
| stmt.raw_bind_parameter(3, 50i32)?; |
| let n = stmt.raw_execute()?; |
| assert_eq!(n, 1); |
| } |
| |
| { |
| let mut stmt = db.prepare("SELECT name, value FROM test WHERE value = ?2")?; |
| stmt.raw_bind_parameter(2, 50)?; |
| let mut rows = stmt.raw_query(); |
| { |
| let row = rows.next()?.unwrap(); |
| let name: String = row.get(0)?; |
| assert_eq!(name, "example"); |
| let value: i32 = row.get(1)?; |
| assert_eq!(value, 50); |
| } |
| assert!(rows.next()?.is_none()); |
| } |
| |
| Ok(()) |
| } |
| |
| #[test] |
| fn test_unbound_parameters_are_reused() -> 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(&[(":x", "one")])?; |
| stmt.execute(&[(":y", "two")])?; |
| |
| let result: String = |
| db.query_row("SELECT x FROM test WHERE y = 'two'", [], |row| row.get(0))?; |
| assert_eq!(result, "one"); |
| Ok(()) |
| } |
| |
| #[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 (?)")?; |
| assert_eq!(stmt.insert([1i32])?, 1); |
| assert_eq!(stmt.insert([2i32])?, 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")?; |
| match multi.insert([]).unwrap_err() { |
| Error::StatementChangedRows(2) => (), |
| err => panic!("Unexpected error {}", err), |
| } |
| Ok(()) |
| } |
| |
| #[test] |
| fn test_insert_different_tables() -> Result<()> { |
| // Test for https://github.com/rusqlite/rusqlite/issues/171 |
| let db = Connection::open_in_memory()?; |
| db.execute_batch( |
| r" |
| CREATE TABLE foo(x INTEGER); |
| CREATE TABLE bar(x INTEGER); |
| ", |
| )?; |
| |
| assert_eq!(db.prepare("INSERT INTO foo VALUES (10)")?.insert([])?, 1); |
| assert_eq!(db.prepare("INSERT INTO bar VALUES (10)")?.insert([])?, 1); |
| Ok(()) |
| } |
| |
| #[test] |
| fn test_exists() -> Result<()> { |
| let db = Connection::open_in_memory()?; |
| let sql = "BEGIN; |
| CREATE TABLE foo(x INTEGER); |
| INSERT INTO foo VALUES(1); |
| INSERT INTO foo VALUES(2); |
| END;"; |
| db.execute_batch(sql)?; |
| let mut stmt = db.prepare("SELECT 1 FROM foo WHERE x = ?")?; |
| assert!(stmt.exists([1i32])?); |
| assert!(stmt.exists([2i32])?); |
| assert!(!stmt.exists([0i32])?); |
| Ok(()) |
| } |
| #[test] |
| fn test_tuple_params() -> Result<()> { |
| let db = Connection::open_in_memory()?; |
| let s = db.query_row("SELECT printf('[%s]', ?)", ("abc",), |r| { |
| r.get::<_, String>(0) |
| })?; |
| assert_eq!(s, "[abc]"); |
| let s = db.query_row( |
| "SELECT printf('%d %s %d', ?, ?, ?)", |
| (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', ?, ?, ?, ?)", |
| (1, "abc", 2i32, 4i64), |
| |r| r.get::<_, String>(0), |
| )?; |
| assert_eq!(s, "1 abc 2 4"); |
| #[rustfmt::skip] |
| let bigtup = ( |
| 0, "a", 1, "b", 2, "c", 3, "d", |
| 4, "e", 5, "f", 6, "g", 7, "h", |
| ); |
| let query = "SELECT printf( |
| '%d %s | %d %s | %d %s | %d %s || %d %s | %d %s | %d %s | %d %s', |
| ?, ?, ?, ?, |
| ?, ?, ?, ?, |
| ?, ?, ?, ?, |
| ?, ?, ?, ? |
| )"; |
| 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"); |
| Ok(()) |
| } |
| |
| #[test] |
| fn test_query_row() -> Result<()> { |
| let db = Connection::open_in_memory()?; |
| let sql = "BEGIN; |
| CREATE TABLE foo(x INTEGER, y INTEGER); |
| INSERT INTO foo VALUES(1, 3); |
| INSERT INTO foo VALUES(2, 4); |
| END;"; |
| db.execute_batch(sql)?; |
| let mut stmt = db.prepare("SELECT y FROM foo WHERE x = ?")?; |
| let y: Result<i64> = stmt.query_row([1i32], |r| r.get(0)); |
| assert_eq!(3i64, y?); |
| Ok(()) |
| } |
| |
| #[test] |
| fn test_query_by_column_name() -> Result<()> { |
| let db = Connection::open_in_memory()?; |
| let sql = "BEGIN; |
| CREATE TABLE foo(x INTEGER, y INTEGER); |
| INSERT INTO foo VALUES(1, 3); |
| END;"; |
| db.execute_batch(sql)?; |
| let mut stmt = db.prepare("SELECT y FROM foo")?; |
| let y: Result<i64> = stmt.query_row([], |r| r.get("y")); |
| assert_eq!(3i64, y?); |
| Ok(()) |
| } |
| |
| #[test] |
| fn test_query_by_column_name_ignore_case() -> Result<()> { |
| let db = Connection::open_in_memory()?; |
| let sql = "BEGIN; |
| CREATE TABLE foo(x INTEGER, y INTEGER); |
| INSERT INTO foo VALUES(1, 3); |
| END;"; |
| db.execute_batch(sql)?; |
| let mut stmt = db.prepare("SELECT y as Y FROM foo")?; |
| let y: Result<i64> = stmt.query_row([], |r| r.get("y")); |
| assert_eq!(3i64, y?); |
| Ok(()) |
| } |
| |
| #[test] |
| #[cfg(feature = "modern_sqlite")] |
| fn test_expanded_sql() -> Result<()> { |
| let db = Connection::open_in_memory()?; |
| let stmt = db.prepare("SELECT ?")?; |
| stmt.bind_parameter(&1, 1)?; |
| assert_eq!(Some("SELECT 1".to_owned()), stmt.expanded_sql()); |
| Ok(()) |
| } |
| |
| #[test] |
| fn test_bind_parameters() -> Result<()> { |
| let db = Connection::open_in_memory()?; |
| // dynamic slice: |
| db.query_row( |
| "SELECT ?1, ?2, ?3", |
| &[&1u8 as &dyn ToSql, &"one", &Some("one")], |
| |row| row.get::<_, u8>(0), |
| )?; |
| // existing collection: |
| let data = vec![1, 2, 3]; |
| db.query_row("SELECT ?1, ?2, ?3", params_from_iter(&data), |row| { |
| row.get::<_, u8>(0) |
| })?; |
| db.query_row( |
| "SELECT ?1, ?2, ?3", |
| params_from_iter(data.as_slice()), |
| |row| row.get::<_, u8>(0), |
| )?; |
| db.query_row("SELECT ?1, ?2, ?3", params_from_iter(data), |row| { |
| row.get::<_, u8>(0) |
| })?; |
| |
| use std::collections::BTreeSet; |
| let data: BTreeSet<String> = ["one", "two", "three"] |
| .iter() |
| .map(|s| (*s).to_string()) |
| .collect(); |
| db.query_row("SELECT ?1, ?2, ?3", params_from_iter(&data), |row| { |
| row.get::<_, String>(0) |
| })?; |
| |
| let data = [0; 3]; |
| db.query_row("SELECT ?1, ?2, ?3", params_from_iter(&data), |row| { |
| row.get::<_, u8>(0) |
| })?; |
| db.query_row("SELECT ?1, ?2, ?3", params_from_iter(data.iter()), |row| { |
| row.get::<_, u8>(0) |
| })?; |
| Ok(()) |
| } |
| |
| #[test] |
| fn test_parameter_name() -> Result<()> { |
| let db = Connection::open_in_memory()?; |
| db.execute_batch("CREATE TABLE test (name TEXT, value INTEGER)")?; |
| let stmt = db.prepare("INSERT INTO test (name, value) VALUES (:name, ?3)")?; |
| assert_eq!(stmt.parameter_name(0), None); |
| assert_eq!(stmt.parameter_name(1), Some(":name")); |
| assert_eq!(stmt.parameter_name(2), None); |
| Ok(()) |
| } |
| |
| #[test] |
| fn test_empty_stmt() -> Result<()> { |
| 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.reset(); |
| assert!(stmt.execute([]).is_err()); |
| Ok(()) |
| } |
| |
| #[test] |
| fn test_comment_stmt() -> Result<()> { |
| let conn = Connection::open_in_memory()?; |
| conn.prepare("/*SELECT 1;*/")?; |
| Ok(()) |
| } |
| |
| #[test] |
| fn test_comment_and_sql_stmt() -> Result<()> { |
| let conn = Connection::open_in_memory()?; |
| let stmt = conn.prepare("/*...*/ SELECT 1;")?; |
| assert_eq!(1, stmt.column_count()); |
| Ok(()) |
| } |
| |
| #[test] |
| fn test_semi_colon_stmt() -> Result<()> { |
| let conn = Connection::open_in_memory()?; |
| let stmt = conn.prepare(";")?; |
| assert_eq!(0, stmt.column_count()); |
| Ok(()) |
| } |
| |
| #[test] |
| fn test_utf16_conversion() -> Result<()> { |
| let db = Connection::open_in_memory()?; |
| 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))?; |
| assert_eq!(expected, actual); |
| Ok(()) |
| } |
| |
| #[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))?; |
| assert_eq!(expected, actual); |
| Ok(()) |
| } |
| |
| #[test] |
| #[cfg(feature = "modern_sqlite")] |
| fn is_explain() -> Result<()> { |
| let db = Connection::open_in_memory()?; |
| let stmt = db.prepare("SELECT 1;")?; |
| assert_eq!(0, stmt.is_explain()); |
| Ok(()) |
| } |
| |
| #[test] |
| #[cfg(all(feature = "modern_sqlite", not(feature = "bundled-sqlcipher")))] // 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); |
| assert_eq!(offset, 7); |
| } |
| err => panic!("Unexpected error {}", err), |
| } |
| Ok(()) |
| } |
| } |