Skip to main content

clt_database/turso/src/
params.rs

1//! This module contains all `Param` related utilities and traits.
2
3use std::borrow::Cow;
4
5use crate::turso::{Error, Result, Value};
6
7mod sealed {
8    pub trait Sealed {}
9}
10
11use sealed::Sealed;
12
13/// Converts some type into parameters that can be passed
14/// to libsql.
15///
16/// The trait is sealed and not designed to be implemented by hand
17/// but instead provides a few ways to use it.
18///
19/// # Passing parameters to libsql
20///
21/// Many functions in this library let you pass parameters to libsql. Doing this
22/// lets you avoid any risk of SQL injection, and is simpler than escaping
23/// things manually. These functions generally contain some parameter that generically
24/// accepts some implementation this trait.
25///
26/// # Positional parameters
27///
28/// These can be supplied in a few ways:
29///
30/// - For heterogeneous parameter lists of 16 or less items a tuple syntax is supported
31///   by doing `(1, "foo")`.
32/// - For hetergeneous parameter lists of 16 or greater, the [`turso::params!`] is supported
33///   by doing `turso::params![1, "foo"]`.
34/// - For homogeneous parameter types (where they are all the same type), const arrays are
35///   supported by doing `[1, 2, 3]`.
36///
37/// # Example (positional)
38///
39/// ```rust,no_run
40/// # use turso::{Connection, params};
41/// # async fn run(conn: Connection) -> turso::Result<()> {
42/// let mut stmt = conn.prepare("INSERT INTO test (a, b) VALUES (?1, ?2)").await?;
43///
44/// // Using a tuple:
45/// stmt.execute((0, "foobar")).await?;
46///
47/// // Using `turso::params!`:
48/// stmt.execute(params![1i32, "blah"]).await?;
49///
50/// // array literal — non-references
51/// stmt.execute([2i32, 3i32]).await?;
52///
53/// // array literal — references
54/// stmt.execute(["foo", "bar"]).await?;
55///
56/// // Slice literal, references:
57/// stmt.execute([2i32, 3i32]).await?;
58///
59/// #    Ok(())
60/// # }
61/// ```
62///
63/// # Named parameters
64///
65/// Named parameter keys must include the SQL prefix used in the statement,
66/// for example `:name`, `@name`, `$name`, or `?1`.
67///
68/// - For heterogeneous parameter lists of 16 or less items a tuple syntax is supported
69///   by doing `((":key1", 1), (":key2", "foo"))`.
70/// - For heterogeneous parameter lists of 16 or greater, the [`turso::params!`] is supported
71///   by doing `turso::named_params![":key1": 1, ":key2": "foo"]`.
72/// - For homogeneous parameter types (where they are all the same type), const arrays are
73///   supported by doing `[(":key1", 1), (":key2", 2), (":key3", 3)]`.
74///
75/// # Example (named)
76///
77/// ```rust,no_run
78/// # use turso::{Connection, named_params};
79/// # async fn run(conn: Connection) -> turso::Result<()> {
80/// let mut stmt = conn.prepare("INSERT INTO test (a, b) VALUES (:key1, :key2)").await?;
81///
82/// // Using a tuple:
83/// stmt.execute(((":key1", 0), (":key2", "foobar"))).await?;
84///
85/// // Using `turso::named_params!`:
86/// stmt.execute(named_params! {":key1": 1i32, ":key2": "blah" }).await?;
87///
88/// // const array:
89/// stmt.execute([(":key1", 2i32), (":key2", 3i32)]).await?;
90///
91/// #   Ok(())
92/// # }
93/// ```
94pub trait IntoParams: Sealed {
95    // Hide this because users should not be implementing this
96    // themselves. We should consider sealing this trait.
97    #[doc(hidden)]
98    fn into_params(self) -> Result<Params>;
99}
100
101#[derive(Debug, Clone)]
102#[doc(hidden)]
103pub enum Params {
104    None,
105    Positional(Vec<Value>),
106    Named(Vec<(Cow<'static, str>, Value)>),
107}
108
109/// Convert an owned iterator into Params.
110///
111/// # Example
112///
113/// ```rust
114/// # use turso::{Connection, params_from_iter, Rows};
115/// # async fn run(conn: &Connection) {
116///
117/// let iter = vec![1, 2, 3];
118///
119/// conn.query(
120///     "SELECT * FROM users WHERE id IN (?1, ?2, ?3)",
121///     params_from_iter(iter)
122/// )
123/// .await
124/// .unwrap();
125/// # }
126/// ```
127pub fn params_from_iter<I>(iter: I) -> impl IntoParams
128where
129    I: IntoIterator,
130    I::Item: IntoValue,
131{
132    iter.into_iter().collect::<Vec<_>>()
133}
134
135impl Sealed for () {}
136impl IntoParams for () {
137    fn into_params(self) -> Result<Params> {
138        Ok(Params::None)
139    }
140}
141
142impl Sealed for Params {}
143impl IntoParams for Params {
144    fn into_params(self) -> Result<Params> {
145        Ok(self)
146    }
147}
148
149impl<T: IntoValue> Sealed for Vec<T> {}
150impl<T: IntoValue> IntoParams for Vec<T> {
151    fn into_params(self) -> Result<Params> {
152        let values = self
153            .into_iter()
154            .map(|i| i.into_value())
155            .collect::<Result<Vec<_>>>()?;
156
157        Ok(Params::Positional(values))
158    }
159}
160
161impl<T: IntoValue> Sealed for Vec<(String, T)> {}
162impl<T: IntoValue> IntoParams for Vec<(String, T)> {
163    fn into_params(self) -> Result<Params> {
164        let values = self
165            .into_iter()
166            .map(|(k, v)| Ok((Cow::Owned(k), v.into_value()?)))
167            .collect::<Result<Vec<_>>>()?;
168
169        Ok(Params::Named(values))
170    }
171}
172
173impl<T: IntoValue, const N: usize> Sealed for [T; N] {}
174impl<T: IntoValue, const N: usize> IntoParams for [T; N] {
175    fn into_params(self) -> Result<Params> {
176        self.into_iter().collect::<Vec<_>>().into_params()
177    }
178}
179
180// Named parameters with static string keys to avoid String allocations.
181impl<T: IntoValue, const N: usize> Sealed for [(&'static str, T); N] {}
182impl<T: IntoValue, const N: usize> IntoParams for [(&'static str, T); N] {
183    fn into_params(self) -> Result<Params> {
184        let values = self
185            .into_iter()
186            .map(|(k, v)| Ok((Cow::Borrowed(k), v.into_value()?)))
187            .collect::<Result<Vec<_>>>()?;
188
189        Ok(Params::Named(values))
190    }
191}
192
193impl<T: IntoValue + Clone, const N: usize> Sealed for &[T; N] {}
194impl<T: IntoValue + Clone, const N: usize> IntoParams for &[T; N] {
195    fn into_params(self) -> Result<Params> {
196        self.iter().cloned().collect::<Vec<_>>().into_params()
197    }
198}
199
200// NOTICE: heavily inspired by rusqlite
201macro_rules! tuple_into_params {
202    ($count:literal : $(($field:tt $ftype:ident)),* $(,)?) => {
203        impl<$($ftype,)*> Sealed for ($($ftype,)*) where $($ftype: IntoValue,)* {}
204        impl<$($ftype,)*> IntoParams for ($($ftype,)*) where $($ftype: IntoValue,)* {
205            fn into_params(self) -> Result<Params> {
206                let params = Params::Positional(vec![$(self.$field.into_value()?),*]);
207                Ok(params)
208            }
209        }
210    }
211}
212
213macro_rules! named_tuple_into_params {
214    ($count:literal : $(($field:tt $ftype:ident)),* $(,)?) => {
215        impl<$($ftype,)*> Sealed for ($((&'static str, $ftype),)*) where $($ftype: IntoValue,)* {}
216        impl<$($ftype,)*> IntoParams for ($((&'static str, $ftype),)*) where $($ftype: IntoValue,)* {
217            fn into_params(self) -> Result<Params> {
218                let params = Params::Named(vec![$((Cow::Borrowed(self.$field.0), self.$field.1.into_value()?)),*]);
219                Ok(params)
220            }
221        }
222    }
223}
224
225named_tuple_into_params!(1: (0 A));
226named_tuple_into_params!(2: (0 A), (1 B));
227named_tuple_into_params!(3: (0 A), (1 B), (2 C));
228named_tuple_into_params!(4: (0 A), (1 B), (2 C), (3 D));
229named_tuple_into_params!(5: (0 A), (1 B), (2 C), (3 D), (4 E));
230named_tuple_into_params!(6: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F));
231named_tuple_into_params!(7: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G));
232named_tuple_into_params!(8: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H));
233named_tuple_into_params!(9: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I));
234named_tuple_into_params!(10: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J));
235named_tuple_into_params!(11: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J), (10 K));
236named_tuple_into_params!(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));
237named_tuple_into_params!(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));
238named_tuple_into_params!(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));
239named_tuple_into_params!(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));
240named_tuple_into_params!(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));
241
242tuple_into_params!(1: (0 A));
243tuple_into_params!(2: (0 A), (1 B));
244tuple_into_params!(3: (0 A), (1 B), (2 C));
245tuple_into_params!(4: (0 A), (1 B), (2 C), (3 D));
246tuple_into_params!(5: (0 A), (1 B), (2 C), (3 D), (4 E));
247tuple_into_params!(6: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F));
248tuple_into_params!(7: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G));
249tuple_into_params!(8: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H));
250tuple_into_params!(9: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I));
251tuple_into_params!(10: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J));
252tuple_into_params!(11: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J), (10 K));
253tuple_into_params!(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));
254tuple_into_params!(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));
255tuple_into_params!(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));
256tuple_into_params!(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));
257tuple_into_params!(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));
258
259// TODO: Should we rename this to `ToSql` which makes less sense but
260// matches the error variant we have in `Error`. Or should we change the
261// error variant to match this breaking the few people that currently use
262// this error variant.
263pub trait IntoValue {
264    fn into_value(self) -> Result<Value>;
265}
266
267impl<T> IntoValue for T
268where
269    T: TryInto<Value>,
270    T::Error: Into<crate::turso::BoxError>,
271{
272    fn into_value(self) -> Result<Value> {
273        self.try_into()
274            .map_err(|e| Error::ToSqlConversionFailure(e.into()))
275    }
276}
277
278impl IntoValue for Result<Value> {
279    fn into_value(self) -> Result<Value> {
280        self
281    }
282}
283
284/// Construct positional params from a heterogeneous set of params types.
285#[macro_export]
286macro_rules! params {
287    () => {
288       ()
289    };
290    ($($value:expr),* $(,)?) => {{
291        use $crate::turso::params::IntoValue;
292        [$($value.into_value()),*]
293
294    }};
295}
296
297/// Construct named params from a heterogeneous set of params types.
298#[macro_export]
299macro_rules! named_params {
300    () => {
301        ()
302    };
303    ($($param_name:literal: $value:expr),* $(,)?) => {{
304        use $crate::turso::params::IntoValue;
305        [$(($param_name, $value.into_value())),*]
306    }};
307}
308
309#[cfg(clt_turso_tests)]
310mod tests {
311    use crate::turso::Value;
312
313    #[test]
314    fn test_serialize_array() {
315        assert_eq!(
316            params!([0; 16])[0].as_ref().unwrap(),
317            &Value::Blob(vec![0; 16])
318        );
319    }
320}