1#![allow(dead_code)]
2use crate::backend::Backend;
3use crate::connection::{AnsiTransactionManager, TransactionManager};
4use crate::pg::Pg;
5use crate::prelude::*;
6use crate::query_builder::{AstPass, QueryBuilder, QueryFragment};
7use crate::result::Error;
8
9#[allow(missing_debug_implementations)] #[must_use = "Transaction builder does nothing unless you call `run` on it"]
20#[cfg(feature = "postgres_backend")]
21pub struct TransactionBuilder<'a, C> {
22 connection: &'a mut C,
23 isolation_level: Option<IsolationLevel>,
24 read_mode: Option<ReadMode>,
25 deferrable: Option<Deferrable>,
26}
27
28impl<'a, C> TransactionBuilder<'a, C>
29where
30 C: Connection<Backend = Pg, TransactionManager = AnsiTransactionManager>,
31{
32 #[diesel_derives::__diesel_public_if(
34 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
35 )]
36 pub(crate) fn new(connection: &'a mut C) -> Self {
37 Self {
38 connection,
39 isolation_level: None,
40 read_mode: None,
41 deferrable: None,
42 }
43 }
44
45 pub fn read_only(mut self) -> Self {
90 self.read_mode = Some(ReadMode::ReadOnly);
91 self
92 }
93
94 pub fn read_write(mut self) -> Self {
134 self.read_mode = Some(ReadMode::ReadWrite);
135 self
136 }
137
138 pub fn deferrable(mut self) -> Self {
156 self.deferrable = Some(Deferrable::Deferrable);
157 self
158 }
159
160 pub fn not_deferrable(mut self) -> Self {
181 self.deferrable = Some(Deferrable::NotDeferrable);
182 self
183 }
184
185 pub fn read_committed(mut self) -> Self {
206 self.isolation_level = Some(IsolationLevel::ReadCommitted);
207 self
208 }
209
210 pub fn repeatable_read(mut self) -> Self {
230 self.isolation_level = Some(IsolationLevel::RepeatableRead);
231 self
232 }
233
234 pub fn serializable(mut self) -> Self {
252 self.isolation_level = Some(IsolationLevel::Serializable);
253 self
254 }
255
256 pub fn run<T, E, F>(&mut self, f: F) -> Result<T, E>
276 where
277 F: FnOnce(&mut C) -> Result<T, E>,
278 E: From<Error>,
279 {
280 let mut query_builder = <Pg as Backend>::QueryBuilder::default();
281 self.to_sql(&mut query_builder, &Pg)?;
282 let sql = query_builder.finish();
283
284 AnsiTransactionManager::begin_transaction_sql(&mut *self.connection, &sql)?;
285 match f(&mut *self.connection) {
286 Ok(value) => {
287 AnsiTransactionManager::commit_transaction(&mut *self.connection)?;
288 Ok(value)
289 }
290 Err(user_error) => {
291 match AnsiTransactionManager::rollback_transaction(&mut *self.connection) {
292 Ok(()) => Err(user_error),
293 Err(Error::BrokenTransactionManager) => {
294 Err(user_error)
297 }
298 Err(rollback_error) => Err(rollback_error.into()),
299 }
300 }
301 }
302 }
303}
304
305impl<C> QueryFragment<Pg> for TransactionBuilder<'_, C> {
306 fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> {
307 out.push_sql("BEGIN TRANSACTION");
308 if let Some(ref isolation_level) = self.isolation_level {
309 isolation_level.walk_ast(out.reborrow())?;
310 }
311 if let Some(ref read_mode) = self.read_mode {
312 read_mode.walk_ast(out.reborrow())?;
313 }
314 if let Some(ref deferrable) = self.deferrable {
315 deferrable.walk_ast(out.reborrow())?;
316 }
317 Ok(())
318 }
319}
320
321#[derive(#[automatically_derived]
impl ::core::fmt::Debug for IsolationLevel {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
IsolationLevel::ReadCommitted => "ReadCommitted",
IsolationLevel::RepeatableRead => "RepeatableRead",
IsolationLevel::Serializable => "Serializable",
})
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for IsolationLevel {
#[inline]
fn clone(&self) -> IsolationLevel { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for IsolationLevel { }Copy)]
322enum IsolationLevel {
323 ReadCommitted,
324 RepeatableRead,
325 Serializable,
326}
327
328impl QueryFragment<Pg> for IsolationLevel {
329 fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> {
330 out.push_sql(" ISOLATION LEVEL ");
331 match *self {
332 IsolationLevel::ReadCommitted => out.push_sql("READ COMMITTED"),
333 IsolationLevel::RepeatableRead => out.push_sql("REPEATABLE READ"),
334 IsolationLevel::Serializable => out.push_sql("SERIALIZABLE"),
335 }
336 Ok(())
337 }
338}
339
340#[derive(#[automatically_derived]
impl ::core::fmt::Debug for ReadMode {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
ReadMode::ReadOnly => "ReadOnly",
ReadMode::ReadWrite => "ReadWrite",
})
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for ReadMode {
#[inline]
fn clone(&self) -> ReadMode { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for ReadMode { }Copy)]
341enum ReadMode {
342 ReadOnly,
343 ReadWrite,
344}
345
346impl QueryFragment<Pg> for ReadMode {
347 fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> {
348 match *self {
349 ReadMode::ReadOnly => out.push_sql(" READ ONLY"),
350 ReadMode::ReadWrite => out.push_sql(" READ WRITE"),
351 }
352 Ok(())
353 }
354}
355
356#[derive(#[automatically_derived]
impl ::core::fmt::Debug for Deferrable {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
Deferrable::Deferrable => "Deferrable",
Deferrable::NotDeferrable => "NotDeferrable",
})
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for Deferrable {
#[inline]
fn clone(&self) -> Deferrable { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for Deferrable { }Copy)]
357enum Deferrable {
358 Deferrable,
359 NotDeferrable,
360}
361
362impl QueryFragment<Pg> for Deferrable {
363 fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> {
364 match *self {
365 Deferrable::Deferrable => out.push_sql(" DEFERRABLE"),
366 Deferrable::NotDeferrable => out.push_sql(" NOT DEFERRABLE"),
367 }
368 Ok(())
369 }
370}
371
372#[cfg(test)]
373#[diesel_test_helper::test]
374fn test_transaction_builder_generates_correct_sql() {
375 extern crate dotenvy;
376
377 macro_rules! assert_sql {
378 ($query:expr, $sql:expr) => {
379 let mut query_builder = <Pg as Backend>::QueryBuilder::default();
380 $query.to_sql(&mut query_builder, &Pg).unwrap();
381 let sql = query_builder.finish();
382 assert_eq!(sql, $sql);
383 };
384 }
385
386 let database_url = dotenvy::var("PG_DATABASE_URL")
387 .or_else(|_| dotenvy::var("DATABASE_URL"))
388 .expect("DATABASE_URL must be set in order to run tests");
389 let mut conn = PgConnection::establish(&database_url).unwrap();
390
391 assert_sql!(conn.build_transaction(), "BEGIN TRANSACTION");
392 assert_sql!(
393 conn.build_transaction().read_only(),
394 "BEGIN TRANSACTION READ ONLY"
395 );
396 assert_sql!(
397 conn.build_transaction().read_write(),
398 "BEGIN TRANSACTION READ WRITE"
399 );
400 assert_sql!(
401 conn.build_transaction().deferrable(),
402 "BEGIN TRANSACTION DEFERRABLE"
403 );
404 assert_sql!(
405 conn.build_transaction().not_deferrable(),
406 "BEGIN TRANSACTION NOT DEFERRABLE"
407 );
408 assert_sql!(
409 conn.build_transaction().read_committed(),
410 "BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED"
411 );
412 assert_sql!(
413 conn.build_transaction().repeatable_read(),
414 "BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ"
415 );
416 assert_sql!(
417 conn.build_transaction().serializable(),
418 "BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE"
419 );
420 assert_sql!(
421 conn.build_transaction()
422 .serializable()
423 .deferrable()
424 .read_only(),
425 "BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE READ ONLY DEFERRABLE"
426 );
427}