tank_postgres/
prepared.rs

1use crate::{PostgresTransaction, ValueWrap, postgres_type_to_value};
2use std::{
3    fmt::{self, Debug, Display},
4    mem,
5};
6use tank_core::{AsValue, Error, Prepared, Result, future::Either};
7use tokio_postgres::{Portal, Statement};
8
9pub struct PostgresPrepared {
10    pub(crate) statement: Statement,
11    pub(crate) index: u64,
12    pub(crate) value: Either<Vec<Option<ValueWrap>>, Portal>,
13}
14
15impl PostgresPrepared {
16    pub(crate) fn new(statement: Statement) -> Self {
17        Self {
18            statement,
19            index: 0,
20            value: Either::Left(vec![]),
21        }
22    }
23    pub(crate) fn is_complete(&self) -> bool {
24        matches!(self.value, Either::Right(..))
25    }
26    pub(crate) async fn complete<'t>(
27        &mut self,
28        transaction: &mut PostgresTransaction<'t>,
29    ) -> Result<Portal> {
30        let Either::Left(params) = &mut self.value else {
31            return Err(Error::msg("The prepared statement is already complete"));
32        };
33        if let Some(i) = params
34            .iter()
35            .enumerate()
36            .find_map(|(i, v)| if v.is_none() { Some(i) } else { None })
37        {
38            return Err(Error::msg(format!("The parameter {} was not set", i)));
39        }
40        let types = self.statement.params();
41        let mut params = mem::take(params);
42        let mut i = 0;
43        for param in &mut params {
44            *param = Some(ValueWrap(
45                mem::take(param)
46                    .unwrap()
47                    .0
48                    .try_as(&postgres_type_to_value(&types[i]))?,
49            ));
50            i += 1;
51        }
52        let portal = transaction
53            .0
54            .bind_raw(&self.statement, params.into_iter().map(Option::unwrap))
55            .await?;
56        self.value = Either::Right(portal.clone());
57        Ok(portal)
58    }
59    pub(crate) fn get_portal(&self) -> Option<Portal> {
60        if let Either::Right(portal) = &self.value {
61            Some(portal.clone())
62        } else {
63            None
64        }
65    }
66}
67
68impl Prepared for PostgresPrepared {
69    fn bind<V: AsValue>(&mut self, value: V) -> Result<&mut Self> {
70        self.bind_index(value, self.index)
71    }
72    fn bind_index<V: AsValue>(&mut self, value: V, index: u64) -> Result<&mut Self> {
73        let Either::Left(params) = &mut self.value else {
74            return Err(Error::msg("The prepared statement is already complete"));
75        };
76        params.resize_with(self.statement.params().len(), || Default::default());
77        params[index as usize] = Some(value.as_value().into());
78        Ok(self)
79    }
80}
81
82impl Display for PostgresPrepared {
83    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84        self.statement.fmt(f)
85    }
86}