Skip to main content

tank_postgres/
prepared.rs

1use crate::{ValueWrap, util::postgres_type_to_value};
2use anyhow::anyhow;
3use std::{
4    borrow::Cow,
5    fmt::{self, Debug, Display},
6    mem,
7};
8use tank_core::{AsValue, Prepared, Result, RowLabels, ValueOwned};
9use tokio_postgres::Statement;
10
11/// Postgres prepared statement.
12#[derive(Debug)]
13pub struct PostgresPrepared {
14    pub(crate) statement: Statement,
15    pub(crate) labels: RowLabels,
16    pub(crate) params: Vec<ValueOwned>,
17    pub(crate) index: u64,
18}
19
20impl PostgresPrepared {
21    pub(crate) fn new(statement: Statement) -> Self {
22        let labels = statement
23            .columns()
24            .iter()
25            .map(|c| c.name().to_string())
26            .collect();
27        Self {
28            statement,
29            labels,
30            params: Vec::new(),
31            index: 0,
32        }
33    }
34
35    #[inline]
36    pub(crate) fn take_params(&mut self) -> Result<Vec<ValueWrap<'static>>> {
37        self.index = 0;
38        if self.params.is_empty() {
39            return Ok(Vec::new());
40        }
41        let types = self.statement.params();
42        mem::take(&mut self.params)
43            .into_iter()
44            .enumerate()
45            .map(|(i, param)| {
46                Ok(ValueWrap(Cow::Owned(
47                    param.try_as(&postgres_type_to_value(&types[i]))?,
48                )))
49            })
50            .collect()
51    }
52}
53
54impl Prepared for PostgresPrepared {
55    fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> {
56        self
57    }
58
59    fn clear_bindings(&mut self) -> Result<&mut Self> {
60        self.params.clear();
61        self.index = 0;
62        Ok(self)
63    }
64
65    fn bind(&mut self, value: impl AsValue) -> Result<&mut Self> {
66        self.bind_index(value, self.index)
67    }
68
69    fn bind_index(&mut self, value: impl AsValue, index: u64) -> Result<&mut Self> {
70        let len = self.statement.params().len();
71        self.params.resize_with(len, Default::default);
72        let target = self.params.get_mut(index as usize).ok_or_else(|| {
73            anyhow!("Index {index} cannot be bound, the query has only {len} parameters")
74        })?;
75        *target = value.as_value().into_owned();
76        self.index = index + 1;
77        Ok(self)
78    }
79}
80
81impl Display for PostgresPrepared {
82    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83        f.write_str("PostgresPrepared: ")?;
84        self.statement.fmt(f)
85    }
86}