diff --git a/src/lib.rs b/src/lib.rs index a45c9a4..f0281e5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -187,7 +187,7 @@ macro_rules! params { /// } /// /// fn add_person(conn: &Connection, person: &Person) -> Result<()> { -/// conn.execute_named( +/// conn.execute( /// "INSERT INTO person (name, age_in_years, data) /// VALUES (:name, :age, :data)", /// named_params!{ @@ -511,7 +511,7 @@ impl Connection { /// ```rust,no_run /// # use rusqlite::{Connection, Result}; /// fn insert(conn: &Connection) -> Result { - /// conn.execute_named( + /// conn.execute( /// "INSERT INTO test (name) VALUES (:name)", /// rusqlite::named_params!{ ":name": "one" }, /// ) diff --git a/src/params.rs b/src/params.rs index 9ac500f..913a462 100644 --- a/src/params.rs +++ b/src/params.rs @@ -5,16 +5,162 @@ mod sealed { /// 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. +/// Trait used for [sets of parameter][params] passed into SQL +/// statements/queries. /// -/// Currently, this trait can only be implemented inside this crate. +/// [params]: https://www.sqlite.org/c3ref/bind_blob.html +/// +/// Note: Currently, this trait can only be implemented inside this crate. +/// Additionally, it's methods (which are `doc(hidden)`) should currently not be +/// considered part of the stable API, although it's possible they will +/// stabilize in the future. +/// +/// # Passing parameters to SQLite +/// +/// Many functions in this library let you pass parameters to SQLite. Doing this +/// lets you avoid any risk of SQL injection, and is simpler than escaping +/// things manually. Aside from deprecated functions and a few helpers, this is +/// indicated by the function taking a generic argument that implements `Params` +/// (this trait). +/// +/// ## Positional parameters +/// +/// For cases where you want to pass a list of parameters where the number of +/// parameters is known at compile time, this can be done in one of the +/// following ways: +/// +/// - Using the [`rusqlite::params!`](crate::params!) macro, e.g. +/// `thing.query(rusqlite::params![1, "foo", bar])`. This is mostly useful for +/// heterogeneous lists of parameters, or lists where the number of parameters +/// exceeds 32. +/// +/// - For small heterogeneous lists of parameters, they can either be passed as: +/// +/// - an array, as in `thing.query([1i32, 2, 3, 4])` or `thing.query(["foo", +/// "bar", "baz"])`. +/// +/// - a reference to an array of references, as in `thing.query(&["foo", +/// "bar", "baz"])` or `thing.query(&[&1i32, &2, &3])`. +/// +/// (Note: in this case we don't implement this for slices for coherence +/// reasons, so it really is only for the "reference to array" types — +/// hence why the number of parameters must be <= 32 or you need to +/// reach for `rusqlite::params!`) +/// +/// Unfortunately, in the current design it's not possible to allow this for +/// references to arrays of non-references (e.g. `&[1i32, 2, 3]`). Code like +/// this should instead either use `params!`, an array literal, a `&[&dyn +/// ToSql]` or if none of those work, [`ParamsFromIter`]. +/// +/// - As a slice of `ToSql` trait object references, e.g. `&[&dyn ToSql]`. This +/// is mostly useful for passing parameter lists around as arguments without +/// having every function take a generic `P: Params`. +/// +/// ### Example (positional) +/// +/// ```rust,no_run +/// # use rusqlite::{Connection, Result, params}; +/// fn update_rows(conn: &Connection) -> Result<()> { +/// let mut stmt = conn.prepare("INSERT INTO test (a, b) VALUES (?, ?)")?; +/// +/// // Using `rusqlite::params!`: +/// stmt.execute(params![1i32, "blah"])?; +/// +/// // array literal — non-references +/// stmt.execute([2i32, 3i32])?; +/// +/// // array literal — references +/// stmt.execute(["foo", "bar"])?; +/// +/// // Slice literal, references: +/// stmt.execute(&[&2i32, &3i32])?; +/// +/// // Note: The types behind the references don't have to be `Sized` +/// stmt.execute(&["foo", "bar"])?; +/// +/// // However, this doesn't work (see above): +/// // stmt.execute(&[1i32, 2i32])?; +/// Ok(()) +/// } +/// ``` +/// +/// ## Named parameters +/// +/// SQLite lets you name parameters using a number of conventions (":foo", +/// "@foo", "$foo"). You can pass named parameters in to SQLite using rusqlite +/// in a few ways: +/// +/// - Using the [`rusqlite::named_params!`](crate::named_params!) macro, as in +/// `stmt.execute(named_params!{ ":name": "foo", ":age": 99 })`. Similar to +/// the `params` macro, this is most useful for heterogeneous lists of +/// parameters, or lists where the number of parameters exceeds 32. +/// +/// - As a slice of `&[(&str, &dyn ToSql)]`. This is what essentially all of +/// these boil down to in the end, conceptually at least. In theory you can +/// pass this as `stmt. +/// +/// - As array references, similar to the positional params. This looks like +/// `thing.query(&[(":foo", &1i32), (":bar", &2i32)])` or +/// `thing.query(&[(":foo", "abc"), (":bar", "def")])`. +/// +/// Note: Unbound named parameters will be left to the value they previously +/// were bound with, falling back to `NULL` for parameters which have never been +/// bound. +/// +/// ### Example (named) +/// +/// ```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)")?; +/// // Using `rusqlite::params!`: +/// stmt.execute(named_params!{ ":key": "one", ":val": 2 })?; +/// // Alternatively: +/// stmt.execute(&[(":key", "three"), (":val", "four")])?; +/// // Or: +/// stmt.execute(&[(":key", &100), (":val", &200)])?; +/// Ok(()) +/// } +/// ``` +/// +/// ## No parameters +/// +/// You can just use an empty array literal for no params. The +/// `rusqlite::NO_PARAMS` constant which was so common in previous versions of +/// this library is no longer needed. +/// +/// ### Example (no parameters) +/// +/// ```rust,no_run +/// # use rusqlite::{Connection, Result, params}; +/// fn delete_all_users(conn: &Connection) -> Result<()> { +/// // Just use an empty array (e.g. `[]`) for no params. +/// conn.execute("DELETE FROM users", [])?; +/// Ok(()) +/// } +/// ``` +/// +/// ## Dynamic parameter list +/// +/// If you have a number of parameters which is unknown at compile time (for +/// example, building a dynamic query at runtime), you have two choices: +/// +/// - Use a `&[&dyn ToSql]`, which is nice if you have one otherwise might be +/// annoying. +/// - Use the [`ParamsFromIter`] type. This essentially lets you wrap an +/// iterator some `T: ToSql` with something that implements `Params`. +/// +/// A lot of the considerations here are similar either way, so you should see +/// the [`ParamsFromIter`] documentation for more info / examples. 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. + // XXX not public api, might not need to expose. + // + // 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<()>; @@ -69,6 +215,10 @@ macro_rules! impl_for_array_ref { } )+}; } + +// Following libstd/libcore's (old) lead, implement this for arrays up to `[_; +// 32]`. Note `[_; 0]` is intentionally omitted for coherence reasons, see the +// note above the impl of `[&dyn ToSql; 0]` for more information. 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 @@ -154,17 +304,21 @@ impl_for_array_ref!( /// 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). +/// surpass [`conn.limit(Limit::SQLITE_LIMIT_VARIABLE_NUMBER)`][limits]), +/// chunking if too large. (Note that the limits api requires rusqlite to have +/// the "limits" feature). /// /// - `repeat_vars` can be implemented in a way that avoids needing to allocate /// a String. /// +/// - Etc... +/// /// [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. +/// parameters you have statically (and if you don't, you're either doing +/// something tricky, or should take a moment to think about the design). #[derive(Clone, Debug)] pub struct ParamsFromIter(I); diff --git a/src/statement.rs b/src/statement.rs index 7c9123c..7285a63 100644 --- a/src/statement.rs +++ b/src/statement.rs @@ -34,11 +34,14 @@ impl Statement<'_> { /// # use rusqlite::{Connection, Result, params}; /// fn update_rows(conn: &Connection) -> Result<()> { /// let mut stmt = conn.prepare("UPDATE foo SET bar = 'baz' WHERE qux = ?")?; - /// + /// // 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. /// stmt.execute(params![1i32])?; - /// // Similarly... + /// // However, it's not required, many cases are fine as: /// stmt.execute(&[&2i32])?; - /// + /// // Or even: + /// stmt.execute([2i32])?; /// Ok(()) /// } /// ``` @@ -46,21 +49,18 @@ impl Statement<'_> { /// ### Use with named parameters /// /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// fn insert(conn: &Connection) -> Result { - /// let mut stmt = conn.prepare("INSERT INTO test (name) VALUES (:name)")?; - /// stmt.execute(&[(":name", &"one")]) - /// } - /// ``` - /// - /// 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 insert(conn: &Connection) -> Result { - /// let mut stmt = conn.prepare("INSERT INTO test (name) VALUES (:name)")?; - /// stmt.execute(named_params!{":name": "one"}) + /// 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(()) /// } /// ``` ///