1use postgres_protocol::message::frontend;
23use crate::{codec::FrontendMessage, connection::RequestMessages, Client, Error, Transaction};
45/// The isolation level of a database transaction.
6#[derive(Debug, Copy, Clone)]
7#[non_exhaustive]
8pub enum IsolationLevel {
9/// Equivalent to `ReadCommitted`.
10ReadUncommitted,
1112/// An individual statement in the transaction will see rows committed before it began.
13ReadCommitted,
1415/// All statements in the transaction will see the same view of rows committed before the first query in the
16 /// transaction.
17RepeatableRead,
1819/// The reads and writes in this transaction must be able to be committed as an atomic "unit" with respect to reads
20 /// and writes of all other concurrent serializable transactions without interleaving.
21Serializable,
22}
2324/// A builder for database transactions.
25pub struct TransactionBuilder<'a> {
26 client: &'a mut Client,
27 isolation_level: Option<IsolationLevel>,
28 read_only: Option<bool>,
29 deferrable: Option<bool>,
30}
3132impl<'a> TransactionBuilder<'a> {
33pub(crate) fn new(client: &'a mut Client) -> TransactionBuilder<'a> {
34 TransactionBuilder {
35 client,
36 isolation_level: None,
37 read_only: None,
38 deferrable: None,
39 }
40 }
4142/// Sets the isolation level of the transaction.
43pub fn isolation_level(mut self, isolation_level: IsolationLevel) -> Self {
44self.isolation_level = Some(isolation_level);
45self
46}
4748/// Sets the access mode of the transaction.
49pub fn read_only(mut self, read_only: bool) -> Self {
50self.read_only = Some(read_only);
51self
52}
5354/// Sets the deferrability of the transaction.
55 ///
56 /// If the transaction is also serializable and read only, creation of the transaction may block, but when it
57 /// completes the transaction is able to run with less overhead and a guarantee that it will not be aborted due to
58 /// serialization failure.
59pub fn deferrable(mut self, deferrable: bool) -> Self {
60self.deferrable = Some(deferrable);
61self
62}
6364/// Begins the transaction.
65 ///
66 /// The transaction will roll back by default - use the `commit` method to commit it.
67pub async fn start(self) -> Result<Transaction<'a>, Error> {
68let mut query = "START TRANSACTION".to_string();
69let mut first = true;
7071if let Some(level) = self.isolation_level {
72 first = false;
7374 query.push_str(" ISOLATION LEVEL ");
75let level = match level {
76 IsolationLevel::ReadUncommitted => "READ UNCOMMITTED",
77 IsolationLevel::ReadCommitted => "READ COMMITTED",
78 IsolationLevel::RepeatableRead => "REPEATABLE READ",
79 IsolationLevel::Serializable => "SERIALIZABLE",
80 };
81 query.push_str(level);
82 }
8384if let Some(read_only) = self.read_only {
85if !first {
86 query.push(',');
87 }
88 first = false;
8990let s = if read_only {
91" READ ONLY"
92} else {
93" READ WRITE"
94};
95 query.push_str(s);
96 }
9798if let Some(deferrable) = self.deferrable {
99if !first {
100 query.push(',');
101 }
102103let s = if deferrable {
104" DEFERRABLE"
105} else {
106" NOT DEFERRABLE"
107};
108 query.push_str(s);
109 }
110111struct RollbackIfNotDone<'me> {
112 client: &'me Client,
113 done: bool,
114 }
115116impl<'a> Drop for RollbackIfNotDone<'a> {
117fn drop(&mut self) {
118if self.done {
119return;
120 }
121122let buf = self.client.inner().with_buf(|buf| {
123 frontend::query("ROLLBACK", buf).unwrap();
124 buf.split().freeze()
125 });
126let _ = self
127.client
128 .inner()
129 .send(RequestMessages::Single(FrontendMessage::Raw(buf)));
130 }
131 }
132133// This is done as `Future` created by this method can be dropped after
134 // `RequestMessages` is synchronously send to the `Connection` by
135 // `batch_execute()`, but before `Responses` is asynchronously polled to
136 // completion. In that case `Transaction` won't be created and thus
137 // won't be rolled back.
138{
139let mut cleaner = RollbackIfNotDone {
140 client: self.client,
141 done: false,
142 };
143self.client.batch_execute(&query).await?;
144 cleaner.done = true;
145 }
146147Ok(Transaction::new(self.client))
148 }
149}