mysql/conn/
query.rs

1// Copyright (c) 2020 rust-mysql-simple contributors
2//
3// Licensed under the Apache License, Version 2.0
4// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
5// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. All files in the project carrying such notice may not be copied,
7// modified, or distributed except according to those terms.
8
9use mysql_common::row::convert::FromRowError;
10
11use std::{convert::TryInto, result::Result as StdResult};
12
13use crate::{
14    conn::{queryable::AsStatement, ConnMut},
15    from_row, from_row_opt,
16    prelude::FromRow,
17    Binary, Error, Params, QueryResult, Result, Text,
18};
19
20/// MySql text query.
21///
22/// This trait covers the set of `query*` methods on the `Queryable` trait.
23/// Please see the corresponding section of the crate level docs for details.
24///
25/// Example:
26///
27/// ```rust
28/// # mysql::doctest_wrapper!(__result, {
29/// use mysql::*;
30/// use mysql::prelude::*;
31/// let pool = Pool::new(get_opts())?;
32///
33/// let num: Option<u32> = "SELECT 42".first(&pool)?;
34///
35/// assert_eq!(num, Some(42));
36/// # });
37/// ```
38pub trait TextQuery: Sized {
39    /// This methods corresponds to `Queryable::query_iter`.
40    fn run<'a, 'b, 'c, C>(self, conn: C) -> Result<QueryResult<'a, 'b, 'c, Text>>
41    where
42        C: TryInto<ConnMut<'a, 'b, 'c>>,
43        Error: From<<C as TryInto<ConnMut<'a, 'b, 'c>>>::Error>;
44
45    /// This methods corresponds to `Queryable::query_first`.
46    fn first<'a, 'b, 'c: 'b, T, C>(self, conn: C) -> Result<Option<T>>
47    where
48        C: TryInto<ConnMut<'a, 'b, 'c>>,
49        Error: From<<C as TryInto<ConnMut<'a, 'b, 'c>>>::Error>,
50        T: FromRow,
51    {
52        self.run(conn)?
53            .next()
54            .map(|row| row.map(from_row))
55            .transpose()
56    }
57
58    /// Same as [`TextQuery::first`] but useful when you not sure what your schema is.
59    fn first_opt<'a, 'b, 'c: 'b, T, C>(self, conn: C) -> Result<Option<StdResult<T, FromRowError>>>
60    where
61        C: TryInto<ConnMut<'a, 'b, 'c>>,
62        Error: From<<C as TryInto<ConnMut<'a, 'b, 'c>>>::Error>,
63        T: FromRow,
64    {
65        self.run(conn)?
66            .next()
67            .map(|row| row.map(from_row_opt))
68            .transpose()
69    }
70
71    /// This methods corresponds to `Queryable::query`.
72    fn fetch<'a, 'b, 'c: 'b, T, C>(self, conn: C) -> Result<Vec<T>>
73    where
74        C: TryInto<ConnMut<'a, 'b, 'c>>,
75        Error: From<<C as TryInto<ConnMut<'a, 'b, 'c>>>::Error>,
76        T: FromRow,
77    {
78        self.run(conn)?.map(|rrow| rrow.map(from_row)).collect()
79    }
80
81    /// Same as [`TextQuery::fetch`] but useful when you not sure what your schema is.
82    fn fetch_opt<'a, 'b, 'c: 'b, T, C>(self, conn: C) -> Result<Vec<StdResult<T, FromRowError>>>
83    where
84        C: TryInto<ConnMut<'a, 'b, 'c>>,
85        Error: From<<C as TryInto<ConnMut<'a, 'b, 'c>>>::Error>,
86        T: FromRow,
87    {
88        self.run(conn)?.map(|rrow| rrow.map(from_row_opt)).collect()
89    }
90
91    /// This methods corresponds to `Queryable::query_fold`.
92    fn fold<'a, 'b, 'c: 'b, T, U, F, C>(self, conn: C, mut init: U, mut next: F) -> Result<U>
93    where
94        C: TryInto<ConnMut<'a, 'b, 'c>>,
95        Error: From<<C as TryInto<ConnMut<'a, 'b, 'c>>>::Error>,
96        T: FromRow,
97        F: FnMut(U, T) -> U,
98    {
99        for row in self.run(conn)? {
100            init = next(init, from_row(row?));
101        }
102
103        Ok(init)
104    }
105
106    /// Same as [`TextQuery::fold`] but useful when you not sure what your schema is.
107    fn fold_opt<'a, 'b, 'c: 'b, T, U, F, C>(self, conn: C, mut init: U, mut next: F) -> Result<U>
108    where
109        C: TryInto<ConnMut<'a, 'b, 'c>>,
110        Error: From<<C as TryInto<ConnMut<'a, 'b, 'c>>>::Error>,
111        T: FromRow,
112        F: FnMut(U, StdResult<T, FromRowError>) -> U,
113    {
114        for row in self.run(conn)? {
115            init = next(init, from_row_opt(row?));
116        }
117
118        Ok(init)
119    }
120
121    /// This methods corresponds to `Queryable::query_map`.
122    fn map<'a, 'b, 'c: 'b, T, U, F, C>(self, conn: C, mut map: F) -> Result<Vec<U>>
123    where
124        C: TryInto<ConnMut<'a, 'b, 'c>>,
125        Error: From<<C as TryInto<ConnMut<'a, 'b, 'c>>>::Error>,
126        T: FromRow,
127        F: FnMut(T) -> U,
128    {
129        self.fold(conn, Vec::new(), |mut acc, row: T| {
130            acc.push(map(row));
131            acc
132        })
133    }
134
135    /// Same as [`TextQuery::map`] but useful when you not sure what your schema is.
136    fn map_opt<'a, 'b, 'c: 'b, T, U, F, C>(self, conn: C, mut map: F) -> Result<Vec<U>>
137    where
138        C: TryInto<ConnMut<'a, 'b, 'c>>,
139        Error: From<<C as TryInto<ConnMut<'a, 'b, 'c>>>::Error>,
140        T: FromRow,
141        F: FnMut(StdResult<T, FromRowError>) -> U,
142    {
143        self.fold_opt(
144            conn,
145            Vec::new(),
146            |mut acc, row: StdResult<T, FromRowError>| {
147                acc.push(map(row));
148                acc
149            },
150        )
151    }
152}
153
154impl<Q: AsRef<str>> TextQuery for Q {
155    fn run<'a, 'b, 'c, C>(self, conn: C) -> Result<QueryResult<'a, 'b, 'c, Text>>
156    where
157        C: TryInto<ConnMut<'a, 'b, 'c>>,
158        Error: From<<C as TryInto<ConnMut<'a, 'b, 'c>>>::Error>,
159    {
160        let mut conn = conn.try_into()?;
161        let meta = conn._query(self.as_ref())?;
162        Ok(QueryResult::new(conn, meta))
163    }
164}
165
166/// Representation of a prepared statement query.
167///
168/// See `BinQuery` for details.
169#[derive(Debug, Clone, PartialEq, Eq)]
170pub struct QueryWithParams<Q, P> {
171    pub query: Q,
172    pub params: P,
173}
174
175/// Helper, that constructs `QueryWithParams`.
176pub trait WithParams: Sized {
177    fn with<P>(self, params: P) -> QueryWithParams<Self, P>;
178}
179
180impl<T: AsRef<str>> WithParams for T {
181    fn with<P>(self, params: P) -> QueryWithParams<Self, P> {
182        QueryWithParams {
183            query: self,
184            params,
185        }
186    }
187}
188
189/// MySql prepared statement query.
190///
191/// This trait covers the set of `exec*` methods on the `Queryable` trait.
192/// Please see the corresponding section of the crate level docs for details.
193///
194/// Example:
195///
196/// ```rust
197/// # mysql::doctest_wrapper!(__result, {
198/// use mysql::*;
199/// use mysql::prelude::*;
200/// let pool = Pool::new(get_opts())?;
201///
202/// let num: Option<u32> = "SELECT ?"
203///     .with((42,))
204///     .first(&pool)?;
205///
206/// assert_eq!(num, Some(42));
207/// # });
208/// ```
209pub trait BinQuery: Sized {
210    /// This methods corresponds to `Queryable::exec_iter`.
211    fn run<'a, 'b, 'c, C>(self, conn: C) -> Result<QueryResult<'a, 'b, 'c, Binary>>
212    where
213        C: TryInto<ConnMut<'a, 'b, 'c>>,
214        Error: From<<C as TryInto<ConnMut<'a, 'b, 'c>>>::Error>;
215
216    /// This methods corresponds to `Queryable::exec_first`.
217    fn first<'a, 'b, 'c: 'b, T, C>(self, conn: C) -> Result<Option<T>>
218    where
219        C: TryInto<ConnMut<'a, 'b, 'c>>,
220        Error: From<<C as TryInto<ConnMut<'a, 'b, 'c>>>::Error>,
221        T: FromRow,
222    {
223        self.run(conn)?
224            .next()
225            .map(|row| row.map(from_row))
226            .transpose()
227    }
228
229    /// Same as [`BinQuery::first`] but useful when you not sure what your schema is.
230    fn first_opt<'a, 'b, 'c: 'b, T, C>(self, conn: C) -> Result<Option<StdResult<T, FromRowError>>>
231    where
232        C: TryInto<ConnMut<'a, 'b, 'c>>,
233        Error: From<<C as TryInto<ConnMut<'a, 'b, 'c>>>::Error>,
234        T: FromRow,
235    {
236        self.run(conn)?
237            .next()
238            .map(|row| row.map(from_row_opt))
239            .transpose()
240    }
241
242    /// This methods corresponds to `Queryable::exec`.
243    fn fetch<'a, 'b, 'c: 'b, T, C>(self, conn: C) -> Result<Vec<T>>
244    where
245        C: TryInto<ConnMut<'a, 'b, 'c>>,
246        Error: From<<C as TryInto<ConnMut<'a, 'b, 'c>>>::Error>,
247        T: FromRow,
248    {
249        self.run(conn)?.map(|rrow| rrow.map(from_row)).collect()
250    }
251
252    /// Same as [`BinQuery::fetch`] but useful when you not sure what your schema is.
253    fn fetch_opt<'a, 'b, 'c: 'b, T, C>(self, conn: C) -> Result<Vec<StdResult<T, FromRowError>>>
254    where
255        C: TryInto<ConnMut<'a, 'b, 'c>>,
256        Error: From<<C as TryInto<ConnMut<'a, 'b, 'c>>>::Error>,
257        T: FromRow,
258    {
259        self.run(conn)?.map(|rrow| rrow.map(from_row_opt)).collect()
260    }
261
262    /// This methods corresponds to `Queryable::exec_fold`.
263    fn fold<'a, 'b, 'c: 'b, T, U, F, C>(self, conn: C, mut init: U, mut next: F) -> Result<U>
264    where
265        C: TryInto<ConnMut<'a, 'b, 'c>>,
266        Error: From<<C as TryInto<ConnMut<'a, 'b, 'c>>>::Error>,
267        T: FromRow,
268        F: FnMut(U, T) -> U,
269    {
270        for row in self.run(conn)? {
271            init = next(init, from_row(row?));
272        }
273
274        Ok(init)
275    }
276
277    /// Same as [`BinQuery::fold`] but useful when you not sure what your schema is.
278    fn fold_opt<'a, 'b, 'c: 'b, T, U, F, C>(self, conn: C, mut init: U, mut next: F) -> Result<U>
279    where
280        C: TryInto<ConnMut<'a, 'b, 'c>>,
281        Error: From<<C as TryInto<ConnMut<'a, 'b, 'c>>>::Error>,
282        T: FromRow,
283        F: FnMut(U, StdResult<T, FromRowError>) -> U,
284    {
285        for row in self.run(conn)? {
286            init = next(init, from_row_opt(row?));
287        }
288
289        Ok(init)
290    }
291
292    /// This methods corresponds to `Queryable::exec_map`.
293    fn map<'a, 'b, 'c: 'b, T, U, F, C>(self, conn: C, mut map: F) -> Result<Vec<U>>
294    where
295        C: TryInto<ConnMut<'a, 'b, 'c>>,
296        Error: From<<C as TryInto<ConnMut<'a, 'b, 'c>>>::Error>,
297        T: FromRow,
298        F: FnMut(T) -> U,
299    {
300        self.fold(conn, Vec::new(), |mut acc, row: T| {
301            acc.push(map(row));
302            acc
303        })
304    }
305
306    /// Same as [`BinQuery::map`] but useful when you not sure what your schema is.
307    fn map_opt<'a, 'b, 'c: 'b, T, U, F, C>(self, conn: C, mut map: F) -> Result<Vec<U>>
308    where
309        C: TryInto<ConnMut<'a, 'b, 'c>>,
310        Error: From<<C as TryInto<ConnMut<'a, 'b, 'c>>>::Error>,
311        T: FromRow,
312        F: FnMut(StdResult<T, FromRowError>) -> U,
313    {
314        self.fold_opt(
315            conn,
316            Vec::new(),
317            |mut acc, row: StdResult<T, FromRowError>| {
318                acc.push(map(row));
319                acc
320            },
321        )
322    }
323}
324
325impl<Q, P> BinQuery for QueryWithParams<Q, P>
326where
327    Q: AsStatement,
328    P: Into<Params>,
329{
330    fn run<'a, 'b, 'c, C>(self, conn: C) -> Result<QueryResult<'a, 'b, 'c, Binary>>
331    where
332        C: TryInto<ConnMut<'a, 'b, 'c>>,
333        Error: From<<C as TryInto<ConnMut<'a, 'b, 'c>>>::Error>,
334    {
335        let mut conn = conn.try_into()?;
336        let statement = self.query.as_statement(&mut *conn)?;
337        let meta = conn._execute(&*statement, self.params.into())?;
338        Ok(QueryResult::new(conn, meta))
339    }
340}
341
342/// Helper trait for batch statement execution.
343///
344/// This trait covers the `Queryable::exec_batch` method.
345/// Please see the corresponding section of the crate level docs for details.
346///
347/// Example:
348///
349/// ```rust
350/// # mysql::doctest_wrapper!(__result, {
351/// use mysql::*;
352/// use mysql::prelude::*;
353/// let pool = Pool::new(get_opts())?;
354///
355/// // This will prepare `DO ?` and execute `DO 0`, `DO 1`, `DO 2` and so on.
356/// "DO ?"
357///     .with((0..10).map(|x| (x,)))
358///     .batch(&pool)?;
359/// # });
360/// ```
361pub trait BatchQuery {
362    fn batch<'a, 'b, 'c: 'b, C>(self, conn: C) -> Result<()>
363    where
364        C: TryInto<ConnMut<'a, 'b, 'c>>,
365        Error: From<<C as TryInto<ConnMut<'a, 'b, 'c>>>::Error>;
366}
367
368impl<Q, I, P> BatchQuery for QueryWithParams<Q, I>
369where
370    Q: AsStatement,
371    I: IntoIterator<Item = P>,
372    P: Into<Params>,
373{
374    /// This methods corresponds to `Queryable::exec_batch`.
375    fn batch<'a, 'b, 'c: 'b, C>(self, conn: C) -> Result<()>
376    where
377        C: TryInto<ConnMut<'a, 'b, 'c>>,
378        Error: From<<C as TryInto<ConnMut<'a, 'b, 'c>>>::Error>,
379    {
380        let mut conn = conn.try_into()?;
381        let statement = self.query.as_statement(&mut *conn)?;
382
383        for params in self.params {
384            let params = params.into();
385            let meta = conn._execute(&*statement, params)?;
386            let mut query_result = QueryResult::<Binary>::new((&mut *conn).into(), meta);
387            while let Some(result_set) = query_result.iter() {
388                for row in result_set {
389                    row?;
390                }
391            }
392        }
393
394        Ok(())
395    }
396}