mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-22 16:29:20 +08:00
Implement Params
for tuples of ToSql up to size 16, and touch up docs
This commit is contained in:
parent
8db9aff358
commit
2ec0b2e8fe
@ -1,5 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rusqlite"
|
name = "rusqlite"
|
||||||
|
# Note: Update version in README.md when you change this.
|
||||||
version = "0.27.0"
|
version = "0.27.0"
|
||||||
authors = ["The rusqlite developers"]
|
authors = ["The rusqlite developers"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
49
README.md
49
README.md
@ -26,11 +26,11 @@ fn main() -> Result<()> {
|
|||||||
|
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"CREATE TABLE person (
|
"CREATE TABLE person (
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
data BLOB
|
data BLOB
|
||||||
)",
|
)",
|
||||||
[],
|
[], // empty list of parameters.
|
||||||
)?;
|
)?;
|
||||||
let me = Person {
|
let me = Person {
|
||||||
id: 0,
|
id: 0,
|
||||||
@ -69,34 +69,45 @@ newer SQLite version; see details below.
|
|||||||
Rusqlite provides several features that are behind [Cargo
|
Rusqlite provides several features that are behind [Cargo
|
||||||
features](https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section). They are:
|
features](https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section). They are:
|
||||||
|
|
||||||
* [`load_extension`](https://docs.rs/rusqlite/~0/rusqlite/struct.LoadExtensionGuard.html)
|
* [`load_extension`](https://docs.rs/rusqlite/latest/rusqlite/struct.LoadExtensionGuard.html)
|
||||||
allows loading dynamic library-based SQLite extensions.
|
allows loading dynamic library-based SQLite extensions.
|
||||||
* [`backup`](https://docs.rs/rusqlite/~0/rusqlite/backup/index.html)
|
|
||||||
|
* [`backup`](https://docs.rs/rusqlite/latest/rusqlite/backup/index.html)
|
||||||
allows use of SQLite's online backup API. Note: This feature requires SQLite 3.6.11 or later.
|
allows use of SQLite's online backup API. Note: This feature requires SQLite 3.6.11 or later.
|
||||||
* [`functions`](https://docs.rs/rusqlite/~0/rusqlite/functions/index.html)
|
|
||||||
|
* [`functions`](https://docs.rs/rusqlite/latest/rusqlite/functions/index.html)
|
||||||
allows you to load Rust closures into SQLite connections for use in queries.
|
allows you to load Rust closures into SQLite connections for use in queries.
|
||||||
Note: This feature requires SQLite 3.7.3 or later.
|
Note: This feature requires SQLite 3.7.3 or later.
|
||||||
|
|
||||||
* `window` for [window function](https://www.sqlite.org/windowfunctions.html) support (`fun(...) OVER ...`). (Implies `functions`.)
|
* `window` for [window function](https://www.sqlite.org/windowfunctions.html) support (`fun(...) OVER ...`). (Implies `functions`.)
|
||||||
* [`trace`](https://docs.rs/rusqlite/~0/rusqlite/trace/index.html)
|
|
||||||
|
* [`trace`](https://docs.rs/rusqlite/latest/rusqlite/trace/index.html)
|
||||||
allows hooks into SQLite's tracing and profiling APIs. Note: This feature
|
allows hooks into SQLite's tracing and profiling APIs. Note: This feature
|
||||||
requires SQLite 3.6.23 or later.
|
requires SQLite 3.6.23 or later.
|
||||||
* [`blob`](https://docs.rs/rusqlite/~0/rusqlite/blob/index.html)
|
|
||||||
|
* [`blob`](https://docs.rs/rusqlite/latest/rusqlite/blob/index.html)
|
||||||
gives `std::io::{Read, Write, Seek}` access to SQL BLOBs. Note: This feature
|
gives `std::io::{Read, Write, Seek}` access to SQL BLOBs. Note: This feature
|
||||||
requires SQLite 3.7.4 or later.
|
requires SQLite 3.7.4 or later.
|
||||||
* [`limits`](https://docs.rs/rusqlite/~0/rusqlite/struct.Connection.html#method.limit)
|
|
||||||
|
* [`limits`](https://docs.rs/rusqlite/latest/rusqlite/struct.Connection.html#method.limit)
|
||||||
allows you to set and retrieve SQLite's per connection limits.
|
allows you to set and retrieve SQLite's per connection limits.
|
||||||
* `chrono` implements [`FromSql`](https://docs.rs/rusqlite/~0/rusqlite/types/trait.FromSql.html)
|
|
||||||
and [`ToSql`](https://docs.rs/rusqlite/~0/rusqlite/types/trait.ToSql.html) for various
|
* `chrono` implements [`FromSql`](https://docs.rs/rusqlite/latest/rusqlite/types/trait.FromSql.html)
|
||||||
|
and [`ToSql`](https://docs.rs/rusqlite/latest/rusqlite/types/trait.ToSql.html) for various
|
||||||
types from the [`chrono` crate](https://crates.io/crates/chrono).
|
types from the [`chrono` crate](https://crates.io/crates/chrono).
|
||||||
* `serde_json` implements [`FromSql`](https://docs.rs/rusqlite/~0/rusqlite/types/trait.FromSql.html)
|
|
||||||
and [`ToSql`](https://docs.rs/rusqlite/~0/rusqlite/types/trait.ToSql.html) for the
|
* `serde_json` implements [`FromSql`](https://docs.rs/rusqlite/latest/rusqlite/types/trait.FromSql.html)
|
||||||
|
and [`ToSql`](https://docs.rs/rusqlite/latest/rusqlite/types/trait.ToSql.html) for the
|
||||||
`Value` type from the [`serde_json` crate](https://crates.io/crates/serde_json).
|
`Value` type from the [`serde_json` crate](https://crates.io/crates/serde_json).
|
||||||
* `time` implements [`FromSql`](https://docs.rs/rusqlite/~0/rusqlite/types/trait.FromSql.html)
|
|
||||||
and [`ToSql`](https://docs.rs/rusqlite/~0/rusqlite/types/trait.ToSql.html) for the
|
* `time` implements [`FromSql`](https://docs.rs/rusqlite/latest/rusqlite/types/trait.FromSql.html)
|
||||||
|
and [`ToSql`](https://docs.rs/rusqlite/latest/rusqlite/types/trait.ToSql.html) for the
|
||||||
`time::OffsetDateTime` type from the [`time` crate](https://crates.io/crates/time).
|
`time::OffsetDateTime` type from the [`time` crate](https://crates.io/crates/time).
|
||||||
* `url` implements [`FromSql`](https://docs.rs/rusqlite/~0/rusqlite/types/trait.FromSql.html)
|
|
||||||
and [`ToSql`](https://docs.rs/rusqlite/~0/rusqlite/types/trait.ToSql.html) for the
|
* `url` implements [`FromSql`](https://docs.rs/rusqlite/latest/rusqlite/types/trait.FromSql.html)
|
||||||
|
and [`ToSql`](https://docs.rs/rusqlite/latest/rusqlite/types/trait.ToSql.html) for the
|
||||||
`Url` type from the [`url` crate](https://crates.io/crates/url).
|
`Url` type from the [`url` crate](https://crates.io/crates/url).
|
||||||
|
|
||||||
* `bundled` uses a bundled version of SQLite. This is a good option for cases where linking to SQLite is complicated, such as Windows.
|
* `bundled` uses a bundled version of SQLite. This is a good option for cases where linking to SQLite is complicated, such as Windows.
|
||||||
* `sqlcipher` looks for the SQLCipher library to link against instead of SQLite. This feature overrides `bundled`.
|
* `sqlcipher` looks for the SQLCipher library to link against instead of SQLite. This feature overrides `bundled`.
|
||||||
* `bundled-sqlcipher` uses a bundled version of SQLCipher. This searches for and links against a system-installed crypto library to provide the crypto implementation.
|
* `bundled-sqlcipher` uses a bundled version of SQLCipher. This searches for and links against a system-installed crypto library to provide the crypto implementation.
|
||||||
|
15
src/lib.rs
15
src/lib.rs
@ -16,10 +16,10 @@
|
|||||||
//!
|
//!
|
||||||
//! conn.execute(
|
//! conn.execute(
|
||||||
//! "CREATE TABLE person (
|
//! "CREATE TABLE person (
|
||||||
//! id INTEGER PRIMARY KEY,
|
//! id INTEGER PRIMARY KEY,
|
||||||
//! name TEXT NOT NULL,
|
//! name TEXT NOT NULL,
|
||||||
//! data BLOB
|
//! data BLOB
|
||||||
//! )",
|
//! )",
|
||||||
//! [],
|
//! [],
|
||||||
//! )?;
|
//! )?;
|
||||||
//! let me = Person {
|
//! let me = Person {
|
||||||
@ -29,7 +29,7 @@
|
|||||||
//! };
|
//! };
|
||||||
//! conn.execute(
|
//! conn.execute(
|
||||||
//! "INSERT INTO person (name, data) VALUES (?1, ?2)",
|
//! "INSERT INTO person (name, data) VALUES (?1, ?2)",
|
||||||
//! params![me.name, me.data],
|
//! (me.name, me.data),
|
||||||
//! )?;
|
//! )?;
|
||||||
//!
|
//!
|
||||||
//! let mut stmt = conn.prepare("SELECT id, name, data FROM person")?;
|
//! let mut stmt = conn.prepare("SELECT id, name, data FROM person")?;
|
||||||
@ -145,7 +145,7 @@ const STATEMENT_CACHE_DEFAULT_CAPACITY: usize = 16;
|
|||||||
#[deprecated = "Use an empty array instead; `stmt.execute(NO_PARAMS)` => `stmt.execute([])`"]
|
#[deprecated = "Use an empty array instead; `stmt.execute(NO_PARAMS)` => `stmt.execute([])`"]
|
||||||
pub const NO_PARAMS: &[&dyn ToSql] = &[];
|
pub const NO_PARAMS: &[&dyn ToSql] = &[];
|
||||||
|
|
||||||
/// A macro making it more convenient to pass heterogeneous or long lists of
|
/// A macro making it more convenient to longer lists of
|
||||||
/// parameters as a `&[&dyn ToSql]`.
|
/// parameters as a `&[&dyn ToSql]`.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -161,8 +161,7 @@ pub const NO_PARAMS: &[&dyn ToSql] = &[];
|
|||||||
///
|
///
|
||||||
/// fn add_person(conn: &Connection, person: &Person) -> Result<()> {
|
/// fn add_person(conn: &Connection, person: &Person) -> Result<()> {
|
||||||
/// conn.execute(
|
/// conn.execute(
|
||||||
/// "INSERT INTO person (name, age_in_years, data)
|
/// "INSERT INTO person(name, age_in_years, data) VALUES (?1, ?2, ?3)",
|
||||||
/// VALUES (?1, ?2, ?3)",
|
|
||||||
/// params![person.name, person.age_in_years, person.data],
|
/// params![person.name, person.age_in_years, person.data],
|
||||||
/// )?;
|
/// )?;
|
||||||
/// Ok(())
|
/// Ok(())
|
||||||
|
126
src/params.rs
126
src/params.rs
@ -31,12 +31,19 @@ use sealed::Sealed;
|
|||||||
/// parameters is known at compile time, this can be done in one of the
|
/// parameters is known at compile time, this can be done in one of the
|
||||||
/// following ways:
|
/// following ways:
|
||||||
///
|
///
|
||||||
|
/// - For small lists of parameters up to 16 items, they may alternatively be
|
||||||
|
/// passed as a tuple, as in `thing.query((1, "foo"))`.
|
||||||
|
///
|
||||||
|
/// This is somewhat inconvenient for a single item, since you need a
|
||||||
|
/// weird-looking trailing comma: `thing.query(("example",))`. That case is
|
||||||
|
/// perhaps more cleanly expressed as `thing.query(["example"])`.
|
||||||
|
///
|
||||||
/// - Using the [`rusqlite::params!`](crate::params!) macro, e.g.
|
/// - Using the [`rusqlite::params!`](crate::params!) macro, e.g.
|
||||||
/// `thing.query(rusqlite::params![1, "foo", bar])`. This is mostly useful for
|
/// `thing.query(rusqlite::params![1, "foo", bar])`. This is mostly useful for
|
||||||
/// heterogeneous lists of parameters, or lists where the number of parameters
|
/// heterogeneous lists where the number of parameters greater than 16, or
|
||||||
/// exceeds 32.
|
/// homogenous lists of paramters where the number of parameters exceeds 32.
|
||||||
///
|
///
|
||||||
/// - For small heterogeneous lists of parameters, they can either be passed as:
|
/// - For small homogeneous lists of parameters, they can either be passed as:
|
||||||
///
|
///
|
||||||
/// - an array, as in `thing.query([1i32, 2, 3, 4])` or `thing.query(["foo",
|
/// - an array, as in `thing.query([1i32, 2, 3, 4])` or `thing.query(["foo",
|
||||||
/// "bar", "baz"])`.
|
/// "bar", "baz"])`.
|
||||||
@ -65,6 +72,9 @@ use sealed::Sealed;
|
|||||||
/// fn update_rows(conn: &Connection) -> Result<()> {
|
/// fn update_rows(conn: &Connection) -> Result<()> {
|
||||||
/// let mut stmt = conn.prepare("INSERT INTO test (a, b) VALUES (?, ?)")?;
|
/// let mut stmt = conn.prepare("INSERT INTO test (a, b) VALUES (?, ?)")?;
|
||||||
///
|
///
|
||||||
|
/// // Using a tuple:
|
||||||
|
/// stmt.execute((0, "foobar"))?;
|
||||||
|
///
|
||||||
/// // Using `rusqlite::params!`:
|
/// // Using `rusqlite::params!`:
|
||||||
/// stmt.execute(params![1i32, "blah"])?;
|
/// stmt.execute(params![1i32, "blah"])?;
|
||||||
///
|
///
|
||||||
@ -127,12 +137,26 @@ use sealed::Sealed;
|
|||||||
///
|
///
|
||||||
/// ## No parameters
|
/// ## No parameters
|
||||||
///
|
///
|
||||||
/// You can just use an empty array literal for no params. The
|
/// You can just use an empty tuple or the empty array literal to run a query
|
||||||
/// `rusqlite::NO_PARAMS` constant which was so common in previous versions of
|
/// that accepts no parameters. (The `rusqlite::NO_PARAMS` constant which was
|
||||||
/// this library is no longer needed (and is now deprecated).
|
/// common in previous versions of this library is no longer needed, and is now
|
||||||
|
/// deprecated).
|
||||||
///
|
///
|
||||||
/// ### Example (no parameters)
|
/// ### Example (no parameters)
|
||||||
///
|
///
|
||||||
|
/// The empty tuple:
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// # use rusqlite::{Connection, Result, params};
|
||||||
|
/// fn delete_all_users(conn: &Connection) -> Result<()> {
|
||||||
|
/// // You may also use `()`.
|
||||||
|
/// conn.execute("DELETE FROM users", ())?;
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// The empty array:
|
||||||
|
///
|
||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// # use rusqlite::{Connection, Result, params};
|
/// # use rusqlite::{Connection, Result, params};
|
||||||
/// fn delete_all_users(conn: &Connection) -> Result<()> {
|
/// fn delete_all_users(conn: &Connection) -> Result<()> {
|
||||||
@ -147,10 +171,11 @@ use sealed::Sealed;
|
|||||||
/// If you have a number of parameters which is unknown at compile time (for
|
/// 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:
|
/// 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
|
/// - Use a `&[&dyn ToSql]`. This is often annoying to construct if you don't
|
||||||
/// annoying.
|
/// already have this type on-hand.
|
||||||
/// - Use the [`ParamsFromIter`] type. This essentially lets you wrap an
|
/// - Use the [`ParamsFromIter`] type. This essentially lets you wrap an
|
||||||
/// iterator some `T: ToSql` with something that implements `Params`.
|
/// iterator some `T: ToSql` with something that implements `Params`. The
|
||||||
|
/// usage of this looks like `rusqlite::params_from_iter(something)`.
|
||||||
///
|
///
|
||||||
/// A lot of the considerations here are similar either way, so you should see
|
/// A lot of the considerations here are similar either way, so you should see
|
||||||
/// the [`ParamsFromIter`] documentation for more info / examples.
|
/// the [`ParamsFromIter`] documentation for more info / examples.
|
||||||
@ -169,14 +194,23 @@ pub trait Params: Sealed {
|
|||||||
// Explicitly impl for empty array. Critically, for `conn.execute([])` to be
|
// Explicitly impl for empty array. Critically, for `conn.execute([])` to be
|
||||||
// unambiguous, this must be the *only* implementation for an empty array. This
|
// unambiguous, this must be the *only* implementation for an empty array. This
|
||||||
// avoids `NO_PARAMS` being a necessary part of the API.
|
// avoids `NO_PARAMS` being a necessary part of the API.
|
||||||
|
//
|
||||||
|
// This sadly prevents `impl<T: ToSql, const N: usize> Params for [T; N]`, which
|
||||||
|
// forces people to use `params![...]` or `rusqlite::params_from_iter` for long
|
||||||
|
// homogenous lists of parameters. This is not that big of a deal, but is
|
||||||
|
// unfortunate, especially because I mostly did it because I wanted a simple
|
||||||
|
// syntax for no-params that didnt require importing -- the empty tuple fits
|
||||||
|
// that nicely, but I didn't think of it until much later.
|
||||||
|
//
|
||||||
|
// Admittedly, if we did have the generic impl, then we *wouldn't* support the
|
||||||
|
// empty array literal as a parameter, since the `T` there would fail to be
|
||||||
|
// inferred. The error message here would probably be quite bad, and so on
|
||||||
|
// further thought, probably would end up causing *more* surprises, not less.
|
||||||
impl Sealed for [&(dyn ToSql + Send + Sync); 0] {}
|
impl Sealed for [&(dyn ToSql + Send + Sync); 0] {}
|
||||||
impl Params for [&(dyn ToSql + Send + Sync); 0] {
|
impl Params for [&(dyn ToSql + Send + Sync); 0] {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn __bind_in(self, stmt: &mut Statement<'_>) -> Result<()> {
|
fn __bind_in(self, stmt: &mut Statement<'_>) -> Result<()> {
|
||||||
// Note: Can't just return `Ok(())` — `Statement::bind_parameters`
|
stmt.ensure_parameter_count(0)
|
||||||
// checks that the right number of params were passed too.
|
|
||||||
// TODO: we should have tests for `Error::InvalidParameterCount`...
|
|
||||||
stmt.bind_parameters(&[] as &[&dyn ToSql])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,6 +230,69 @@ impl Params for &[(&str, &dyn ToSql)] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Manual impls for the empty and singleton tuple, although the rest are covered
|
||||||
|
// by macros.
|
||||||
|
impl Sealed for () {}
|
||||||
|
impl Params for () {
|
||||||
|
#[inline]
|
||||||
|
fn __bind_in(self, stmt: &mut Statement<'_>) -> Result<()> {
|
||||||
|
stmt.ensure_parameter_count(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// I'm pretty sure you could tweak the `single_tuple_impl` to accept this.
|
||||||
|
impl<T: ToSql> Sealed for (T,) {}
|
||||||
|
impl<T: ToSql> Params for (T,) {
|
||||||
|
#[inline]
|
||||||
|
fn __bind_in(self, stmt: &mut Statement<'_>) -> Result<()> {
|
||||||
|
stmt.ensure_parameter_count(1)?;
|
||||||
|
stmt.raw_bind_parameter(1, self.0)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! single_tuple_impl {
|
||||||
|
($count:literal : $(($field:tt $ftype:ident)),* $(,)?) => {
|
||||||
|
impl<$($ftype,)*> Sealed for ($($ftype,)*) where $($ftype: ToSql,)* {}
|
||||||
|
impl<$($ftype,)*> Params for ($($ftype,)*) where $($ftype: ToSql,)* {
|
||||||
|
fn __bind_in(self, stmt: &mut Statement<'_>) -> Result<()> {
|
||||||
|
stmt.ensure_parameter_count($count)?;
|
||||||
|
$({
|
||||||
|
debug_assert!($field < $count);
|
||||||
|
stmt.raw_bind_parameter($field + 1, self.$field)?;
|
||||||
|
})+
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We use a the macro for the rest, but don't bother with trying to implement it
|
||||||
|
// in a single invocation (it's possible to do, but my attempts were almost the
|
||||||
|
// same amount of code as just writing it out this way, and much more dense --
|
||||||
|
// it is a more complicated case than the TryFrom macro we have for row->tuple).
|
||||||
|
//
|
||||||
|
// Note that going up to 16 (rather than the 12 that the impls in the stdlib
|
||||||
|
// usually support) is just because we did the same in the `TryFrom<Row>` impl.
|
||||||
|
// I didn't catch that then, but there's no reason to remove it, and it seems
|
||||||
|
// nice to be consistent here; this way putting data in the database and getting
|
||||||
|
// data out of the database are more symmetric in a (mostly superficial) sense.
|
||||||
|
single_tuple_impl!(2: (0 A), (1 B));
|
||||||
|
single_tuple_impl!(3: (0 A), (1 B), (2 C));
|
||||||
|
single_tuple_impl!(4: (0 A), (1 B), (2 C), (3 D));
|
||||||
|
single_tuple_impl!(5: (0 A), (1 B), (2 C), (3 D), (4 E));
|
||||||
|
single_tuple_impl!(6: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F));
|
||||||
|
single_tuple_impl!(7: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G));
|
||||||
|
single_tuple_impl!(8: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H));
|
||||||
|
single_tuple_impl!(9: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I));
|
||||||
|
single_tuple_impl!(10: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J));
|
||||||
|
single_tuple_impl!(11: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J), (10 K));
|
||||||
|
single_tuple_impl!(12: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J), (10 K), (11 L));
|
||||||
|
single_tuple_impl!(13: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J), (10 K), (11 L), (12 M));
|
||||||
|
single_tuple_impl!(14: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J), (10 K), (11 L), (12 M), (13 N));
|
||||||
|
single_tuple_impl!(15: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J), (10 K), (11 L), (12 M), (13 N), (14 O));
|
||||||
|
single_tuple_impl!(16: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J), (10 K), (11 L), (12 M), (13 N), (14 O), (15 P));
|
||||||
|
|
||||||
macro_rules! impl_for_array_ref {
|
macro_rules! impl_for_array_ref {
|
||||||
($($N:literal)+) => {$(
|
($($N:literal)+) => {$(
|
||||||
// These are already generic, and there's a shedload of them, so lets
|
// These are already generic, and there's a shedload of them, so lets
|
||||||
@ -225,6 +322,9 @@ macro_rules! impl_for_array_ref {
|
|||||||
// Following libstd/libcore's (old) lead, implement this for arrays up to `[_;
|
// Following libstd/libcore's (old) lead, implement this for arrays up to `[_;
|
||||||
// 32]`. Note `[_; 0]` is intentionally omitted for coherence reasons, see the
|
// 32]`. Note `[_; 0]` is intentionally omitted for coherence reasons, see the
|
||||||
// note above the impl of `[&dyn ToSql; 0]` for more information.
|
// note above the impl of `[&dyn ToSql; 0]` for more information.
|
||||||
|
//
|
||||||
|
// Note that this unfortunately means we can't use const generics here, but I
|
||||||
|
// don't really think it matters -- users who hit that can use `params!` anyway.
|
||||||
impl_for_array_ref!(
|
impl_for_array_ref!(
|
||||||
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
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
|
18 19 20 21 22 23 24 25 26 27 29 30 31 32
|
||||||
|
@ -33,19 +33,47 @@ impl Statement<'_> {
|
|||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// # use rusqlite::{Connection, Result, params};
|
/// # use rusqlite::{Connection, Result, params};
|
||||||
/// fn update_rows(conn: &Connection) -> Result<()> {
|
/// fn update_rows(conn: &Connection) -> Result<()> {
|
||||||
/// let mut stmt = conn.prepare("UPDATE foo SET bar = 'baz' WHERE qux = ?")?;
|
/// 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
|
/// // 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
|
/// // all have the same type, or if there are more than 32 parameters
|
||||||
/// // at once.
|
/// // at once, but it can be used in other cases.
|
||||||
/// stmt.execute(params![1i32])?;
|
/// stmt.execute(params![1i32])?;
|
||||||
/// // However, it's not required, many cases are fine as:
|
/// // However, it's not required, many cases are fine as:
|
||||||
/// stmt.execute(&[&2i32])?;
|
/// stmt.execute(&[&2i32])?;
|
||||||
/// // Or even:
|
/// // Or even:
|
||||||
/// stmt.execute([2i32])?;
|
/// stmt.execute([2i32])?;
|
||||||
|
/// // If you really want to, this is an option as well.
|
||||||
|
/// stmt.execute((2i32,))?;
|
||||||
/// Ok(())
|
/// 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
|
/// ### Use with named parameters
|
||||||
///
|
///
|
||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
@ -566,6 +594,16 @@ impl Statement<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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]
|
#[inline]
|
||||||
pub(crate) fn bind_parameters_named<T: ?Sized + ToSql>(
|
pub(crate) fn bind_parameters_named<T: ?Sized + ToSql>(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -606,9 +644,14 @@ impl Statement<'_> {
|
|||||||
/// - binding named and positional parameters in the same query.
|
/// - binding named and positional parameters in the same query.
|
||||||
/// - separating parameter binding from query execution.
|
/// - separating parameter binding from query execution.
|
||||||
///
|
///
|
||||||
/// Statements that have had their parameters bound this way should be
|
/// In general, statements that have had *any* parameters bound this way
|
||||||
/// queried or executed by [`Statement::raw_query`] or
|
/// should have *all* parameters bound this way, and be queried or executed
|
||||||
/// [`Statement::raw_execute`]. Other functions are not guaranteed to work.
|
/// 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
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -1266,6 +1309,41 @@ mod test {
|
|||||||
assert!(!stmt.exists([0i32])?);
|
assert!(!stmt.exists([0i32])?);
|
||||||
Ok(())
|
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]
|
#[test]
|
||||||
fn test_query_row() -> Result<()> {
|
fn test_query_row() -> Result<()> {
|
||||||
|
Loading…
Reference in New Issue
Block a user