libsql/
params.rs

1//! This module contains all `Param` related utilities and traits.
2
3use crate::{Error, Result, Value};
4
5mod sealed {
6    pub trait Sealed {}
7}
8
9use sealed::Sealed;
10
11/// Converts some type into parameters that can be passed
12/// to libsql.
13///
14/// The trait is sealed and not designed to be implemented by hand
15/// but instead provides a few ways to use it.
16///
17/// # Passing parameters to libsql
18///
19/// Many functions in this library let you pass parameters to libsql. Doing this
20/// lets you avoid any risk of SQL injection, and is simpler than escaping
21/// things manually. These functions generally contain some paramter that generically
22/// accepts some implementation this trait.
23///
24/// # Positional parameters
25///
26/// These can be supplied in a few ways:
27///
28/// - For heterogeneous parameter lists of 16 or less items a tuple syntax is supported
29///     by doing `(1, "foo")`.
30/// - For hetergeneous parameter lists of 16 or greater, the [`libsql::params!`] is supported
31///     by doing `libsql::params![1, "foo"]`.
32/// - For homogeneous paramter types (where they are all the same type), const arrays are
33///     supported by doing `[1, 2, 3]`.
34///
35/// # Example (positional)
36///
37/// ```rust,no_run
38/// # use libsql::{Connection, params};
39/// # async fn run(conn: Connection) -> libsql::Result<()> {
40/// let mut stmt = conn.prepare("INSERT INTO test (a, b) VALUES (?1, ?2)").await?;
41///
42/// // Using a tuple:
43/// stmt.execute((0, "foobar")).await?;
44///
45/// // Using `libsql::params!`:
46/// stmt.execute(params![1i32, "blah"]).await?;
47///
48/// // array literal — non-references
49/// stmt.execute([2i32, 3i32]).await?;
50///
51/// // array literal — references
52/// stmt.execute(["foo", "bar"]).await?;
53///
54/// // Slice literal, references:
55/// stmt.execute([2i32, 3i32]).await?;
56///
57/// #    Ok(())
58/// # }
59/// ```
60///
61/// # Named paramters
62///
63/// - For heterogeneous parameter lists of 16 or less items a tuple syntax is supported
64///     by doing `(("key1", 1), ("key2", "foo"))`.
65/// - For hetergeneous parameter lists of 16 or greater, the [`libsql::params!`] is supported
66///     by doing `libsql::named_params!["key1": 1, "key2": "foo"]`.
67/// - For homogeneous paramter types (where they are all the same type), const arrays are
68///     supported by doing `[("key1", 1), ("key2, 2), ("key3", 3)]`.
69///
70/// # Example (named)
71///
72/// ```rust,no_run
73/// # use libsql::{Connection, named_params};
74/// # async fn run(conn: Connection) -> libsql::Result<()> {
75/// let mut stmt = conn.prepare("INSERT INTO test (a, b) VALUES (:key1, :key2)").await?;
76///
77/// // Using a tuple:
78/// stmt.execute(((":key1", 0), (":key2", "foobar"))).await?;
79///
80/// // Using `libsql::named_params!`:
81/// stmt.execute(named_params! {":key1": 1i32, ":key2": "blah" }).await?;
82///
83/// // const array:
84/// stmt.execute([(":key1", 2i32), (":key2", 3i32)]).await?;
85///
86/// #   Ok(())
87/// # }
88/// ```
89pub trait IntoParams: Sealed {
90    // Hide this because users should not be implementing this
91    // themselves. We should consider sealing this trait.
92    #[doc(hidden)]
93    fn into_params(self) -> Result<Params>;
94}
95
96#[derive(Debug, Clone)]
97#[doc(hidden)]
98pub enum Params {
99    None,
100    Positional(Vec<Value>),
101    Named(Vec<(String, Value)>),
102}
103
104/// Convert an owned iterator into Params.
105///
106/// # Example
107///
108/// ```rust
109/// # use libsql::{Connection, params_from_iter, Rows};
110/// # async fn run(conn: &Connection) {
111///
112/// let iter = vec![1, 2, 3];
113///
114/// conn.query(
115///     "SELECT * FROM users WHERE id IN (?1, ?2, ?3)",
116///     params_from_iter(iter)
117/// )
118/// .await
119/// .unwrap();
120/// # }
121/// ```
122pub fn params_from_iter<I>(iter: I) -> impl IntoParams
123where
124    I: IntoIterator,
125    I::Item: IntoValue,
126{
127    iter.into_iter().collect::<Vec<_>>()
128}
129
130impl Sealed for () {}
131impl IntoParams for () {
132    fn into_params(self) -> Result<Params> {
133        Ok(Params::None)
134    }
135}
136
137impl Sealed for Params {}
138impl IntoParams for Params {
139    fn into_params(self) -> Result<Params> {
140        Ok(self)
141    }
142}
143
144impl Sealed for &Params {}
145impl IntoParams for &Params {
146    fn into_params(self) -> Result<Params> {
147        Ok(self.clone())
148    }
149}
150
151impl<T: IntoValue> Sealed for Vec<T> {}
152impl<T: IntoValue> IntoParams for Vec<T> {
153    fn into_params(self) -> Result<Params> {
154        let values = self
155            .into_iter()
156            .map(|i| i.into_value())
157            .collect::<Result<Vec<_>>>()?;
158
159        Ok(Params::Positional(values))
160    }
161}
162
163impl<T: IntoValue> Sealed for Vec<(String, T)> {}
164impl<T: IntoValue> IntoParams for Vec<(String, T)> {
165    fn into_params(self) -> Result<Params> {
166        let values = self
167            .into_iter()
168            .map(|(k, v)| Ok((k, v.into_value()?)))
169            .collect::<Result<Vec<_>>>()?;
170
171        Ok(Params::Named(values))
172    }
173}
174
175impl<T: IntoValue, const N: usize> Sealed for [T; N] {}
176impl<T: IntoValue, const N: usize> IntoParams for [T; N] {
177    fn into_params(self) -> Result<Params> {
178        self.into_iter().collect::<Vec<_>>().into_params()
179    }
180}
181
182impl<T: IntoValue, const N: usize> Sealed for [(&str, T); N] {}
183impl<T: IntoValue, const N: usize> IntoParams for [(&str, T); N] {
184    fn into_params(self) -> Result<Params> {
185        self.into_iter()
186            // TODO: Pretty unfortunate that we need to allocate here when we know
187            // the str is likely 'static. Maybe we should convert our param names
188            // to be `Cow<'static, str>`?
189            .map(|(k, v)| Ok((k.to_string(), v.into_value()?)))
190            .collect::<Result<Vec<_>>>()?
191            .into_params()
192    }
193}
194
195impl<T: IntoValue + Clone, const N: usize> Sealed for &[T; N] {}
196impl<T: IntoValue + Clone, const N: usize> IntoParams for &[T; N] {
197    fn into_params(self) -> Result<Params> {
198        self.iter().cloned().collect::<Vec<_>>().into_params()
199    }
200}
201
202// NOTICE: heavily inspired by rusqlite
203macro_rules! tuple_into_params {
204    ($count:literal : $(($field:tt $ftype:ident)),* $(,)?) => {
205        impl<$($ftype,)*> Sealed for ($($ftype,)*) where $($ftype: IntoValue,)* {}
206        impl<$($ftype,)*> IntoParams for ($($ftype,)*) where $($ftype: IntoValue,)* {
207            fn into_params(self) -> Result<Params> {
208                let params = Params::Positional(vec![$(self.$field.into_value()?),*]);
209                Ok(params)
210            }
211        }
212    }
213}
214
215macro_rules! named_tuple_into_params {
216    ($count:literal : $(($field:tt $ftype:ident)),* $(,)?) => {
217        impl<$($ftype,)*> Sealed for ($((&str, $ftype),)*) where $($ftype: IntoValue,)* {}
218        impl<$($ftype,)*> IntoParams for ($((&str, $ftype),)*) where $($ftype: IntoValue,)* {
219            fn into_params(self) -> Result<Params> {
220                let params = Params::Named(vec![$((self.$field.0.to_string(), self.$field.1.into_value()?)),*]);
221                Ok(params)
222            }
223        }
224    }
225}
226
227named_tuple_into_params!(2: (0 A), (1 B));
228named_tuple_into_params!(3: (0 A), (1 B), (2 C));
229named_tuple_into_params!(4: (0 A), (1 B), (2 C), (3 D));
230named_tuple_into_params!(5: (0 A), (1 B), (2 C), (3 D), (4 E));
231named_tuple_into_params!(6: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F));
232named_tuple_into_params!(7: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G));
233named_tuple_into_params!(8: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H));
234named_tuple_into_params!(9: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I));
235named_tuple_into_params!(10: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J));
236named_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));
237named_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));
238named_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));
239named_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));
240named_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));
241named_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));
242
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::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#[cfg(feature = "replication")]
285impl From<Params> for libsql_replication::rpc::proxy::query::Params {
286    fn from(params: Params) -> Self {
287        use libsql_replication::rpc::proxy;
288
289        match params {
290            Params::None => proxy::query::Params::Positional(proxy::Positional::default()),
291            Params::Positional(values) => {
292                let values = values
293                    .iter()
294                    .map(|v| bincode::serialize(v).unwrap())
295                    .map(|data| proxy::Value { data })
296                    .collect::<Vec<_>>();
297                proxy::query::Params::Positional(proxy::Positional { values })
298            }
299            Params::Named(values) => {
300                let (names, values) = values
301                    .into_iter()
302                    .map(|(name, value)| {
303                        let data = bincode::serialize(&value).unwrap();
304                        let value = proxy::Value { data };
305                        (name, value)
306                    })
307                    .unzip();
308
309                proxy::query::Params::Named(proxy::Named { names, values })
310            }
311        }
312    }
313}
314
315/// Construct positional params from a hetergeneous set of params types.
316#[macro_export]
317macro_rules! params {
318    () => {
319       ()
320    };
321    ($($value:expr),* $(,)?) => {{
322        use $crate::params::IntoValue;
323        [$($value.into_value()),*]
324
325    }};
326}
327
328/// Construct named params from a hetergeneous set of params types.
329#[macro_export]
330macro_rules! named_params {
331    () => {
332        ()
333    };
334    ($($param_name:literal: $value:expr),* $(,)?) => {{
335        use $crate::params::IntoValue;
336        [$(($param_name, $value.into_value())),*]
337    }};
338}
339
340#[cfg(test)]
341mod tests {
342    use crate::Value;
343
344    #[test]
345    fn test_serialize_array() {
346        assert_eq!(
347            params!([0; 16])[0].as_ref().unwrap(),
348            &Value::Blob(vec![0; 16])
349        );
350    }
351}