Skip to main content

tank_mysql/
prepared.rs

1use crate::ValueWrap;
2use mysql_async::Statement;
3use std::{
4    borrow::Cow,
5    fmt::{self, Debug, Display},
6    mem,
7};
8use tank_core::{AsValue, Error, Prepared, Result, Value};
9
10#[derive(Debug)]
11/// Prepared statement wrapper for MySQL/MariaDB.
12///
13/// Stores the underlying `mysql_async::Statement`, accumulated parameter `Value`s and the next bind index.
14/// Implements `Prepared` for use by the `Executor` implementations.
15pub struct MySQLPrepared {
16    pub(crate) statement: Statement,
17    pub(crate) params: Vec<Value>,
18    pub(crate) index: u64,
19}
20
21impl MySQLPrepared {
22    pub(crate) fn new(statement: Statement) -> Self {
23        Self {
24            statement,
25            params: Vec::new(),
26            index: 0,
27        }
28    }
29    pub(crate) fn take_params(&mut self) -> Result<mysql_async::Params> {
30        self.index = 0;
31        Ok(mysql_async::Params::Positional(
32            mem::take(&mut self.params)
33                .into_iter()
34                .map(|v| ValueWrap(Cow::Owned(v)).try_into())
35                .collect::<Result<_>>()?,
36        ))
37    }
38}
39
40impl Prepared for MySQLPrepared {
41    fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> {
42        self
43    }
44    fn clear_bindings(&mut self) -> Result<&mut Self> {
45        self.params.clear();
46        self.index = 0;
47        Ok(self)
48    }
49    fn bind(&mut self, value: impl AsValue) -> Result<&mut Self> {
50        self.bind_index(value, self.index)
51    }
52    fn bind_index(&mut self, value: impl AsValue, index: u64) -> Result<&mut Self> {
53        let len = self.statement.num_params();
54        if self.params.is_empty() {
55            self.params.resize_with(len as _, Default::default);
56        }
57        let target = self
58            .params
59            .get_mut(index as usize)
60            .ok_or(Error::msg(format!(
61                "Index {index} cannot be bound, the query has only {len} parameters",
62            )))?;
63        *target = value.as_value();
64        self.index = index + 1;
65        Ok(self)
66    }
67}
68
69impl Display for MySQLPrepared {
70    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71        f.write_str("MySQLPrepared: ")?;
72        self.statement.fmt(f)
73    }
74}