xitca_postgres/transaction/
builder.rs

1use crate::{client::ClientBorrowMut, error::Error, execute::Execute, prepare::Prepare, query::Query};
2
3use super::Transaction;
4
5/// The isolation level of a database transaction.
6#[derive(Debug, Copy, Clone)]
7#[non_exhaustive]
8pub enum IsolationLevel {
9    /// Equivalent to `ReadCommitted`.
10    ReadUncommitted,
11    /// An individual statement in the transaction will see rows committed before it began.
12    ReadCommitted,
13    /// All statements in the transaction will see the same view of rows committed before the first query in the
14    /// transaction.
15    RepeatableRead,
16    /// The reads and writes in this transaction must be able to be committed as an atomic "unit" with respect to reads
17    /// and writes of all other concurrent serializable transactions without interleaving.
18    Serializable,
19}
20
21impl IsolationLevel {
22    const PREFIX: &str = " ISOLATION LEVEL ";
23    const READ_UNCOMMITTED: &str = "READ UNCOMMITTED,";
24    const READ_COMMITTED: &str = "READ COMMITTED,";
25    const REPEATABLE_READ: &str = "REPEATABLE READ,";
26    const SERIALIZABLE: &str = "SERIALIZABLE,";
27
28    fn write(self, str: &mut String) {
29        str.reserve(const { Self::PREFIX.len() + Self::READ_UNCOMMITTED.len() });
30        str.push_str(Self::PREFIX);
31        str.push_str(match self {
32            IsolationLevel::ReadUncommitted => Self::READ_UNCOMMITTED,
33            IsolationLevel::ReadCommitted => Self::READ_COMMITTED,
34            IsolationLevel::RepeatableRead => Self::REPEATABLE_READ,
35            IsolationLevel::Serializable => Self::SERIALIZABLE,
36        });
37    }
38}
39
40/// A builder for database transactions.
41pub struct TransactionBuilder {
42    isolation_level: Option<IsolationLevel>,
43    read_only: Option<bool>,
44    deferrable: Option<bool>,
45}
46
47impl TransactionBuilder {
48    pub(crate) fn new() -> Self {
49        Self {
50            isolation_level: None,
51            read_only: None,
52            deferrable: None,
53        }
54    }
55
56    /// Sets the isolation level of the transaction.
57    pub fn isolation_level(mut self, isolation_level: IsolationLevel) -> Self {
58        self.isolation_level = Some(isolation_level);
59        self
60    }
61
62    /// Sets the access mode of the transaction.
63    pub fn read_only(mut self, read_only: bool) -> Self {
64        self.read_only = Some(read_only);
65        self
66    }
67
68    /// Sets the deferrability of the transaction.
69    ///
70    /// If the transaction is also serializable and read only, creation of the transaction may block, but when it
71    /// completes the transaction is able to run with less overhead and a guarantee that it will not be aborted due to
72    /// serialization failure.
73    pub fn deferrable(mut self, deferrable: bool) -> Self {
74        self.deferrable = Some(deferrable);
75        self
76    }
77
78    /// Begins the transaction.
79    ///
80    /// The transaction will roll back by default - use the `commit` method to commit it.
81    pub async fn begin<C>(self, cli: &mut C) -> Result<Transaction<C>, Error>
82    where
83        C: Prepare + Query + ClientBorrowMut,
84    {
85        // marker check to ensure exclusive borrowing Client. see ClientBorrowMut for detail
86        let _c = cli._borrow_mut();
87
88        let mut query = String::from("START TRANSACTION");
89
90        let Self {
91            isolation_level,
92            read_only,
93            deferrable,
94        } = self;
95
96        if let Some(isolation_level) = isolation_level {
97            isolation_level.write(&mut query);
98        }
99
100        if let Some(read_only) = read_only {
101            let s = if read_only { " READ ONLY," } else { " READ WRITE," };
102            query.push_str(s);
103        }
104
105        if let Some(deferrable) = deferrable {
106            let s = if deferrable { " DEFERRABLE" } else { " NOT DEFERRABLE" };
107            query.push_str(s);
108        }
109
110        if query.ends_with(',') {
111            query.pop();
112        }
113
114        query.as_str().execute(cli).await.map(|_| Transaction::new(cli))
115    }
116}