duckdb/appender_params.rs
1use crate::{Appender, Result, ToSql};
2
3mod sealed {
4 /// This trait exists just to ensure that the only impls of `trait Params`
5 /// that are allowed are ones in this crate.
6 pub trait Sealed {}
7}
8use sealed::Sealed;
9
10/// Trait used for [sets of parameter][params] passed into SQL
11/// statements/queries.
12///
13/// [params]: https://duckdb.org/docs/stable/clients/c/prepared.html
14///
15/// Note: Currently, this trait can only be implemented inside this crate.
16/// Additionally, it's methods (which are `doc(hidden)`) should currently not be
17/// considered part of the stable API, although it's possible they will
18/// stabilize in the future.
19///
20/// # Passing parameters to SQLite
21///
22/// Many functions in this library let you pass parameters to SQLite. Doing this
23/// lets you avoid any risk of SQL injection, and is simpler than escaping
24/// things manually. Aside from deprecated functions and a few helpers, this is
25/// indicated by the function taking a generic argument that implements `Params`
26/// (this trait).
27///
28/// ## Positional parameters
29///
30/// For cases where you want to pass a list of parameters where the number of
31/// parameters is known at compile time, this can be done in one of the
32/// following ways:
33///
34/// - Using the [`duckdb::params!`](crate::params!) macro, e.g.
35/// `thing.query(duckdb::params![1, "foo", bar])`. This is mostly useful for
36/// heterogeneous lists of parameters, or lists where the number of parameters
37/// exceeds 32.
38///
39/// - For small heterogeneous lists of parameters, they can either be passed as:
40///
41/// - an array, as in `thing.query([1i32, 2, 3, 4])` or `thing.query(["foo",
42/// "bar", "baz"])`.
43///
44/// - a reference to an array of references, as in `thing.query(&["foo",
45/// "bar", "baz"])` or `thing.query(&[&1i32, &2, &3])`.
46///
47/// (Note: in this case we don't implement this for slices for coherence
48/// reasons, so it really is only for the "reference to array" types —
49/// hence why the number of parameters must be <= 32 or you need to
50/// reach for `duckdb::params!`)
51///
52/// Unfortunately, in the current design it's not possible to allow this for
53/// references to arrays of non-references (e.g. `&[1i32, 2, 3]`). Code like
54/// this should instead either use `params!`, an array literal, a `&[&dyn
55/// ToSql]` or if none of those work, [`ParamsFromIter`].
56///
57/// - As a slice of `ToSql` trait object references, e.g. `&[&dyn ToSql]`. This
58/// is mostly useful for passing parameter lists around as arguments without
59/// having every function take a generic `P: Params`.
60///
61/// ### Example (positional)
62///
63/// ```rust,no_run
64/// # use duckdb::{Connection, Result, params};
65/// fn update_rows(conn: &Connection) -> Result<()> {
66/// let mut stmt = conn.prepare("INSERT INTO test (a, b) VALUES (?, ?)")?;
67///
68/// // Using `duckdb::params!`:
69/// stmt.execute(params![1i32, "blah"])?;
70///
71/// // array literal — non-references
72/// stmt.execute([2i32, 3i32])?;
73///
74/// // array literal — references
75/// stmt.execute(["foo", "bar"])?;
76///
77/// // Slice literal, references:
78/// stmt.execute(&[&2i32, &3i32])?;
79///
80/// // Note: The types behind the references don't have to be `Sized`
81/// stmt.execute(&["foo", "bar"])?;
82///
83/// // However, this doesn't work (see above):
84/// // stmt.execute(&[1i32, 2i32])?;
85/// Ok(())
86/// }
87/// ```
88///
89/// ## No parameters
90///
91/// You can just use an empty array literal for no params. The
92/// `duckdb::NO_PARAMS` constant which was so common in previous versions of
93/// this library is no longer needed (and is now deprecated).
94///
95/// ### Example (no parameters)
96///
97/// ```rust,no_run
98/// # use duckdb::{Connection, Result, params};
99/// fn delete_all_users(conn: &Connection) -> Result<()> {
100/// // Just use an empty array (e.g. `[]`) for no params.
101/// conn.execute("DELETE FROM users", [])?;
102/// Ok(())
103/// }
104/// ```
105///
106/// ## Dynamic parameter list
107///
108/// If you have a number of parameters which is unknown at compile time (for
109/// example, building a dynamic query at runtime), you have two choices:
110///
111/// - Use a `&[&dyn ToSql]`, which is nice if you have one otherwise might be
112/// annoying.
113/// - Use the [`ParamsFromIter`] type. This essentially lets you wrap an
114/// iterator some `T: ToSql` with something that implements `Params`.
115///
116/// A lot of the considerations here are similar either way, so you should see
117/// the [`ParamsFromIter`] documentation for more info / examples.
118pub trait AppenderParams: Sealed {
119 // XXX not public api, might not need to expose.
120 //
121 // Binds the parameters to the statement. It is unlikely calling this
122 // explicitly will do what you want. Please use `Statement::query` or
123 // similar directly.
124 //
125 // For now, just hide the function in the docs...
126 #[doc(hidden)]
127 fn __bind_in(self, stmt: &mut Appender<'_>) -> Result<()>;
128}
129
130// Explicitly impl for empty array. Critically, for `conn.execute([])` to be
131// unambiguous, this must be the *only* implementation for an empty array. This
132// avoids `NO_PARAMS` being a necessary part of the API.
133impl Sealed for [&dyn ToSql; 0] {}
134impl AppenderParams for [&dyn ToSql; 0] {
135 #[inline]
136 fn __bind_in(self, stmt: &mut Appender<'_>) -> Result<()> {
137 // Note: Can't just return `Ok(())` — `Statement::bind_parameters`
138 // checks that the right number of params were passed too.
139 // TODO: we should have tests for `Error::InvalidParameterCount`...
140 stmt.bind_parameters(&[] as &[&dyn ToSql])
141 }
142}
143
144impl Sealed for &[&dyn ToSql] {}
145impl AppenderParams for &[&dyn ToSql] {
146 #[inline]
147 fn __bind_in(self, stmt: &mut Appender<'_>) -> Result<()> {
148 stmt.bind_parameters(self)
149 }
150}
151
152macro_rules! impl_for_array_ref {
153 ($($N:literal)+) => {$(
154 // These are already generic, and there's a shedload of them, so lets
155 // avoid the compile time hit from making them all inline for now.
156 impl<T: ToSql + ?Sized> Sealed for &[&T; $N] {}
157 impl<T: ToSql + ?Sized> AppenderParams for &[&T; $N] {
158 fn __bind_in(self, stmt: &mut Appender<'_>) -> Result<()> {
159 stmt.bind_parameters(self)
160 }
161 }
162 impl<T: ToSql> Sealed for [T; $N] {}
163 impl<T: ToSql> AppenderParams for [T; $N] {
164 #[inline]
165 fn __bind_in(self, stmt: &mut Appender<'_>) -> Result<()> {
166 stmt.bind_parameters(&self)
167 }
168 }
169 )+};
170}
171
172// Following libstd/libcore's (old) lead, implement this for arrays up to `[_;
173// 32]`. Note `[_; 0]` is intentionally omitted for coherence reasons, see the
174// note above the impl of `[&dyn ToSql; 0]` for more information.
175//
176// For tables with more than 32 columns, users should use:
177// - `&[&dyn ToSql]` for dynamic parameter binding (recommended)
178// - `params!` macro
179// - `appender_params_from_iter`
180impl_for_array_ref!(
181 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
182 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
183);
184
185/// Adapter type which allows any iterator over [`ToSql`] values to implement
186/// [`Params`].
187///
188/// This struct is created by the [`params_from_iter`] function.
189///
190/// This can be useful if you have something like an `&[String]` (of unknown
191/// length), and you want to use them with an API that wants something
192/// implementing `Params`. This way, you can avoid having to allocate storage
193/// for something like a `&[&dyn ToSql]`.
194///
195/// This essentially is only ever actually needed when dynamically generating
196/// SQL — static SQL (by definition) has the number of parameters known
197/// statically. As dynamically generating SQL is itself pretty advanced, this
198/// API is itself for advanced use cases (See "Realistic use case" in the
199/// examples).
200///
201/// # Example
202///
203/// ## Basic usage
204///
205/// ```rust,no_run
206/// use duckdb::{Connection, Result, params_from_iter};
207/// use std::collections::BTreeSet;
208///
209/// fn query(conn: &Connection, ids: &BTreeSet<String>) -> Result<()> {
210/// assert_eq!(ids.len(), 3, "Unrealistic sample code");
211///
212/// let mut stmt = conn.prepare("SELECT * FROM users WHERE id IN (?, ?, ?)")?;
213/// let _rows = stmt.query(params_from_iter(ids.iter()))?;
214///
215/// // use _rows...
216/// Ok(())
217/// }
218/// ```
219///
220/// ## Realistic use case
221///
222/// Here's how you'd use `ParamsFromIter` to call [`Statement::exists`] with a
223/// dynamic number of parameters.
224///
225/// ```rust,no_run
226/// use duckdb::{Connection, Result};
227///
228/// pub fn any_active_users(conn: &Connection, usernames: &[String]) -> Result<bool> {
229/// if usernames.is_empty() {
230/// return Ok(false);
231/// }
232///
233/// // Note: `repeat_vars` never returns anything attacker-controlled, so
234/// // it's fine to use it in a dynamically-built SQL string.
235/// let vars = repeat_vars(usernames.len());
236///
237/// let sql = format!(
238/// // In practice this would probably be better as an `EXISTS` query.
239/// "SELECT 1 FROM user WHERE is_active AND name IN ({}) LIMIT 1",
240/// vars,
241/// );
242/// let mut stmt = conn.prepare(&sql)?;
243/// stmt.exists(duckdb::params_from_iter(usernames))
244/// }
245///
246/// // Helper function to return a comma-separated sequence of `?`.
247/// // - `repeat_vars(0) => panic!(...)`
248/// // - `repeat_vars(1) => "?"`
249/// // - `repeat_vars(2) => "?,?"`
250/// // - `repeat_vars(3) => "?,?,?"`
251/// // - ...
252/// fn repeat_vars(count: usize) -> String {
253/// assert_ne!(count, 0);
254/// let mut s = "?,".repeat(count);
255/// // Remove trailing comma
256/// s.pop();
257/// s
258/// }
259/// ```
260///
261/// That is fairly complex, and even so would need even more work to be fully
262/// production-ready:
263///
264/// - production code should ensure `usernames` isn't so large that it will
265/// surpass [`conn.limit(Limit::SQLITE_LIMIT_VARIABLE_NUMBER)`][limits]),
266/// chunking if too large. (Note that the limits api requires duckdb to have
267/// the "limits" feature).
268///
269/// - `repeat_vars` can be implemented in a way that avoids needing to allocate
270/// a String.
271///
272/// - Etc...
273///
274/// [limits]: crate::Connection::limit
275///
276/// This complexity reflects the fact that `ParamsFromIter` is mainly intended
277/// for advanced use cases — most of the time you should know how many
278/// parameters you have statically (and if you don't, you're either doing
279/// something tricky, or should take a moment to think about the design).
280#[derive(Clone, Debug)]
281pub struct AppenderParamsFromIter<I>(I);
282
283/// Constructor function for a [`ParamsFromIter`]. See its documentation for
284/// more.
285#[inline]
286pub fn appender_params_from_iter<I>(iter: I) -> AppenderParamsFromIter<I>
287where
288 I: IntoIterator,
289 I::Item: ToSql,
290{
291 AppenderParamsFromIter(iter)
292}
293
294impl<I> Sealed for AppenderParamsFromIter<I>
295where
296 I: IntoIterator,
297 I::Item: ToSql,
298{
299}
300
301impl<I> AppenderParams for AppenderParamsFromIter<I>
302where
303 I: IntoIterator,
304 I::Item: ToSql,
305{
306 #[inline]
307 fn __bind_in(self, stmt: &mut Appender<'_>) -> Result<()> {
308 stmt.bind_parameters(self.0)
309 }
310}