Skip to main content

qusql_mysql/
bind.rs

1//! Provide support for binding arguments to queries
2use crate::constants::type_;
3use bytes::{BufMut, BytesMut};
4use thiserror::Error;
5
6/// Error type returned by [Bind::bind]
7#[derive(Error, Debug)]
8pub enum BindError {
9    /// To many argument has been bound to the query
10    #[error("to many arguments given")]
11    TooManyArgumentsBound,
12    /// Not enough arguments has been bound to the query
13    #[error("missing argument")]
14    TooFewArgumentsBound,
15    /// Error converting between
16    #[error("try from int")]
17    TryFromInt(#[from] std::num::TryFromIntError),
18}
19
20const _: () = {
21    assert!(size_of::<BindError>() <= 8);
22};
23
24/// Result type returned by [Bind::bind]
25pub type BindResult<T> = Result<T, BindError>;
26
27/// Writer used to to compose packages to
28pub struct Writer<'a>(&'a mut BytesMut);
29
30impl<'a> Writer<'a> {
31    /// Construct a new writer writing into w
32    #[allow(unused)]
33    pub(crate) fn new(w: &'a mut BytesMut) -> Self {
34        Writer(w)
35    }
36
37    /// Append a u8 to the package
38    #[inline]
39    pub fn put_u8(&mut self, v: u8) {
40        self.0.put_u8(v);
41    }
42
43    /// Append a u16 to the package
44    #[inline]
45    pub fn put_u16(&mut self, v: u16) {
46        self.0.put_u16_le(v);
47    }
48
49    /// Append a u24 to the package
50    #[inline]
51    pub fn put_u24(&mut self, v: u32) {
52        self.0.put_u8((v & 0xFF) as u8);
53        self.0.put_u8(((v >> 8) & 0xFF) as u8);
54        self.0.put_u8(((v >> 16) & 0xFF) as u8);
55    }
56
57    /// Append a u32 to the package
58    #[inline]
59    pub fn put_u32(&mut self, v: u32) {
60        self.0.put_u32_le(v);
61    }
62
63    /// Append a u64 to the package
64    #[inline]
65    pub fn put_u64(&mut self, v: u64) {
66        self.0.put_u64_le(v);
67    }
68
69    /// Append a i8 to the package
70    #[inline]
71    pub fn put_i8(&mut self, v: i8) {
72        self.0.put_i8(v);
73    }
74
75    /// Append a i16 to the package
76    #[inline]
77    pub fn put_i16(&mut self, v: i16) {
78        self.0.put_i16_le(v);
79    }
80
81    /// Append a i32 to the package
82    #[inline]
83    pub fn put_i32(&mut self, v: i32) {
84        self.0.put_i32_le(v);
85    }
86
87    /// Append a i64 to the package
88    #[inline]
89    pub fn put_i64(&mut self, v: i64) {
90        self.0.put_i64_le(v);
91    }
92
93    /// Append a f32 to the package
94    #[inline]
95    pub fn put_f32(&mut self, v: f32) {
96        self.0.put_f32_le(v);
97    }
98
99    /// Append a f64 to the package
100    #[inline]
101    pub fn put_f64(&mut self, v: f64) {
102        self.0.put_f64_le(v);
103    }
104
105    /// Append a variable encode length to the package
106    ///
107    /// See <https://mariadb.com/docs/server/reference/clientserver-protocol/protocol-data-types#length-encoded-integers>
108    #[inline]
109    pub fn put_lenenc(&mut self, v: u64) {
110        if v < 0xFB {
111            self.put_u8(v as u8);
112        } else if v <= 0xFFFF {
113            self.put_u8(0xFC);
114            self.put_u16(v as u16);
115        } else if v <= 0xFFFFFF {
116            self.put_u8(0xFD);
117            self.put_u24(v as u32);
118        } else {
119            self.put_u8(0xFE);
120            self.put_u64(v);
121        }
122    }
123
124    /// Append the bytes in the slice to the package
125    pub fn put_slice(&mut self, src: &[u8]) {
126        self.0.put_slice(src);
127    }
128}
129
130/// Bind a parameter to a query.
131///
132/// See <https://mariadb.com/docs/server/reference/clientserver-protocol/3-binary-protocol-prepared-statements/server-response-packets-binary-protocol/packet_bindata>
133/// to see how each type should be encoded
134pub trait Bind {
135    /// Should the unsigned flag be set for the value
136    const UNSIGNED: bool = false;
137    /// The type of the value encode as defined in [crate::constants::type_]
138    const TYPE: u8;
139    /// Bind this value as the next value to the query.
140    ///
141    /// Return true if the value is set, and false it it is null.
142    fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool>;
143}
144
145/// Bind a [u8] as a unsigned [type_::TINY]
146impl Bind for u8 {
147    const UNSIGNED: bool = true;
148    const TYPE: u8 = type_::TINY;
149    #[inline]
150    fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
151        writer.put_u8(*self);
152        Ok(true)
153    }
154}
155
156/// Bind a [i8] as a signed [type_::TINY]
157impl Bind for i8 {
158    const TYPE: u8 = type_::TINY;
159    #[inline]
160    fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
161        writer.put_i8(*self);
162        Ok(true)
163    }
164}
165
166/// Bind a [u16] as a unsigned [type_::SHORT]
167impl Bind for u16 {
168    const UNSIGNED: bool = true;
169    const TYPE: u8 = type_::SHORT;
170    #[inline]
171    fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
172        writer.put_u16(*self);
173        Ok(true)
174    }
175}
176
177/// Bind a [i16] as a signed [type_::SHORT]
178impl Bind for i16 {
179    const TYPE: u8 = type_::SHORT;
180    #[inline]
181    fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
182        writer.put_i16(*self);
183        Ok(true)
184    }
185}
186
187/// Bind a [u32] as a unsigned [type_::LONG]
188impl Bind for u32 {
189    const UNSIGNED: bool = true;
190    const TYPE: u8 = type_::LONG;
191    #[inline]
192    fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
193        writer.put_u32(*self);
194        Ok(true)
195    }
196}
197
198/// Bind a [i32] as a signed [type_::LONG]
199impl Bind for i32 {
200    const TYPE: u8 = type_::LONG;
201    #[inline]
202    fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
203        writer.put_i32(*self);
204        Ok(true)
205    }
206}
207
208/// Bind a [u64] as a unsigned [type_::LONG_LONG]
209impl Bind for u64 {
210    const UNSIGNED: bool = true;
211    const TYPE: u8 = type_::LONG_LONG;
212    #[inline]
213    fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
214        writer.put_u64(*self);
215        Ok(true)
216    }
217}
218
219/// Bind a [i64] as a signed [type_::LONG_LONG]
220impl Bind for i64 {
221    const TYPE: u8 = type_::LONG_LONG;
222    #[inline]
223    fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
224        writer.put_i64(*self);
225        Ok(true)
226    }
227}
228
229/// Bind a [f32] as a [type_::FLOAT]
230impl Bind for f32 {
231    const TYPE: u8 = type_::FLOAT;
232    #[inline]
233    fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
234        writer.put_f32(*self);
235        Ok(true)
236    }
237}
238
239/// Bind a [f64] as a [type_::DOUBLE]
240impl Bind for f64 {
241    const TYPE: u8 = type_::DOUBLE;
242    #[inline]
243    fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
244        writer.put_f64(*self);
245        Ok(true)
246    }
247}
248
249/// Bind a [bool] as a [type_::TINY]
250impl Bind for bool {
251    const UNSIGNED: bool = true;
252    const TYPE: u8 = type_::TINY;
253    #[inline]
254    fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
255        writer.put_u8(*self as u8);
256        Ok(true)
257    }
258}
259
260/// Bind a [String] as a [type_::STRING]
261impl Bind for String {
262    const TYPE: u8 = type_::STRING;
263    #[inline]
264    fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
265        writer.put_lenenc(self.len() as u64);
266        writer.put_slice(self.as_bytes());
267        Ok(true)
268    }
269}
270
271/// Bind a &[str] as a [type_::STRING]
272impl Bind for str {
273    const TYPE: u8 = type_::STRING;
274    #[inline]
275    fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
276        writer.put_lenenc(self.len() as u64);
277        writer.put_slice(self.as_bytes());
278        Ok(true)
279    }
280}
281
282/// Bind a [`Vec<u8>`] as a [type_::BLOB]
283impl Bind for Vec<u8> {
284    const TYPE: u8 = type_::BLOB;
285    #[inline]
286    fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
287        writer.put_lenenc(self.len() as u64);
288        writer.put_slice(self);
289        Ok(true)
290    }
291}
292
293/// Bind a &[[u8]] as a [type_::BLOB]
294impl Bind for [u8] {
295    const TYPE: u8 = type_::BLOB;
296    #[inline]
297    fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
298        writer.put_lenenc(self.len() as u64);
299        writer.put_slice(self);
300        Ok(true)
301    }
302}
303
304/// Bind an [`Option<T>`] as T if it is [Some], otherwise as Null
305impl<T: Bind> Bind for Option<T> {
306    const TYPE: u8 = T::TYPE;
307    const UNSIGNED: bool = T::UNSIGNED;
308
309    #[inline]
310    fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
311        match self {
312            Some(v) => v.bind(writer),
313            None => Ok(false),
314        }
315    }
316}
317
318/// Bind arbitrary references
319impl<T: Bind + ?Sized> Bind for &T {
320    const TYPE: u8 = T::TYPE;
321    const UNSIGNED: bool = T::UNSIGNED;
322
323    #[inline]
324    fn bind(&self, writer: &mut Writer<'_>) -> BindResult<bool> {
325        (*self).bind(writer)
326    }
327}
328
329/// Bind used for args that can also handle lists
330pub trait ListBind {
331    /// The contained bind type
332    type T: Bind + ?Sized;
333
334    /// Get the singular bind value.
335    /// Panics if list_length is not None
336    fn single(&self) -> &Self::T;
337
338    /// Get a bind bind value. Panics if list_length is None or if idx >= list_length
339    fn get(&self, idx: usize) -> &Self::T;
340
341    /// If the value is a list return the length of the list
342    fn list_length(&self) -> Option<usize>;
343}
344
345impl<T: Bind + ?Sized> ListBind for T {
346    type T = T;
347
348    #[inline]
349    fn single(&self) -> &Self::T {
350        self
351    }
352
353    #[inline]
354    fn get(&self, _: usize) -> &Self::T {
355        panic!("Singular")
356    }
357
358    #[inline]
359    fn list_length(&self) -> Option<usize> {
360        None
361    }
362}
363
364/// Bind to a _LIST_
365pub struct List<'a, T> {
366    /// The content of the list
367    inner: &'a [T],
368}
369
370impl<'a, T: Bind> ListBind for List<'a, T> {
371    type T = T;
372
373    #[inline]
374    fn single(&self) -> &Self::T {
375        panic!("List")
376    }
377
378    #[inline]
379    fn get(&self, idx: usize) -> &Self::T {
380        &self.inner[idx]
381    }
382
383    #[inline]
384    fn list_length(&self) -> Option<usize> {
385        Some(self.inner.len())
386    }
387}
388
389impl<'a, T: Bind> ListBind for &List<'a, T> {
390    type T = T;
391
392    #[inline]
393    fn single(&self) -> &Self::T {
394        panic!("List")
395    }
396
397    #[inline]
398    fn get(&self, idx: usize) -> &Self::T {
399        &self.inner[idx]
400    }
401
402    #[inline]
403    fn list_length(&self) -> Option<usize> {
404        Some(self.inner.len())
405    }
406}
407
408/// Produce a [List] object that can be used as an argument to a _LIST_ argument
409/// Assuming that the `list_hack` feature is set
410/// ```no_run
411/// use qusql_mysql::{Connection, ExecutorExt, Executor, list, ConnectionError};
412///
413/// async fn test(conn: &mut Connection) -> Result<(), ConnectionError> {
414///     let vs: &[i32] = &[1,2,55];
415///     let rows: Vec<(&str, )> = conn.fetch_all(
416///         "SELECT `t` FROM `table` WHERE v IN (_LIST_)", (list(vs), )).await?;
417///     Ok(())
418/// }
419/// ```
420#[cfg(feature = "list_hack")]
421pub fn list<'a, T: Bind>(v: &'a [T]) -> List<'a, T> {
422    List { inner: v }
423}
424
425/// Produce a [List] object that can be used as an argument to a _LIST_ argument
426/// Assuming that the `list_hack` feature is set
427#[cfg(not(feature = "list_hack"))]
428pub fn list<'a, T: Bind>(_: &'a [T]) -> std::convert::Infallible {
429    panic!("The list_hack feature is not")
430}