xitca_postgres/transaction/
builder.rs1use crate::{client::ClientBorrowMut, error::Error, execute::Execute, prepare::Prepare, query::Query};
2
3use super::Transaction;
4
5#[derive(Debug, Copy, Clone)]
7#[non_exhaustive]
8pub enum IsolationLevel {
9 ReadUncommitted,
11 ReadCommitted,
13 RepeatableRead,
16 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
40pub struct TransactionBuilder {
42 isolation_level: Option<IsolationLevel>,
43 read_only: Option<bool>,
44 deferrable: Option<bool>,
45}
46
47impl TransactionBuilder {
48 pub const fn new() -> Self {
49 Self {
50 isolation_level: None,
51 read_only: None,
52 deferrable: None,
53 }
54 }
55
56 pub fn isolation_level(mut self, isolation_level: IsolationLevel) -> Self {
58 self.isolation_level = Some(isolation_level);
59 self
60 }
61
62 pub fn read_only(mut self, read_only: bool) -> Self {
64 self.read_only = Some(read_only);
65 self
66 }
67
68 pub fn deferrable(mut self, deferrable: bool) -> Self {
74 self.deferrable = Some(deferrable);
75 self
76 }
77
78 pub async fn begin<C>(self, mut cli: C) -> Result<Transaction<C>, Error>
82 where
83 C: Prepare + Query + ClientBorrowMut,
84 {
85 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}
117
118#[cfg(test)]
119mod test {
120 use std::sync::Arc;
121
122 use crate::{
123 Client, Error, Postgres,
124 dev::{ClientBorrowMut, Encode, Prepare, Query, Response},
125 types::{Oid, Type},
126 };
127
128 use super::TransactionBuilder;
129
130 #[tokio::test]
131 async fn client_borrow_mut() {
132 #[derive(Clone)]
133 struct PanicCli(Arc<Client>);
134
135 impl PanicCli {
136 fn new(cli: Client) -> Self {
137 Self(Arc::new(cli))
138 }
139 }
140
141 impl Query for PanicCli {
142 fn _send_encode_query<S>(&self, stmt: S) -> Result<(S::Output, Response), crate::Error>
143 where
144 S: Encode,
145 {
146 self.0._send_encode_query(stmt)
147 }
148 }
149
150 impl Prepare for PanicCli {
151 async fn _get_type(&self, oid: Oid) -> Result<Type, Error> {
152 self.0._get_type(oid).await
153 }
154 fn _get_type_blocking(&self, oid: Oid) -> Result<Type, Error> {
155 self.0._get_type_blocking(oid)
156 }
157 }
158
159 impl ClientBorrowMut for PanicCli {
160 fn _borrow_mut(&mut self) -> &mut Client {
161 Arc::get_mut(&mut self.0).unwrap()
162 }
163 }
164
165 let (cli, drv) = Postgres::new("postgres://postgres:postgres@localhost:5432")
166 .connect()
167 .await
168 .unwrap();
169
170 tokio::spawn(drv.into_future());
171
172 let mut cli = PanicCli::new(cli);
173
174 {
175 let _tx = TransactionBuilder::new().begin(&mut cli).await.unwrap();
176 }
177
178 let res = tokio::spawn(async move {
179 let _cli2 = cli.clone();
180 let _tx = TransactionBuilder::new().begin(cli).await.unwrap();
181 })
182 .await
183 .err()
184 .unwrap();
185
186 assert!(res.is_panic());
187 }
188}