1use crate::connection::Connection;
2use crate::result::{Error, QueryResult};
3use alloc::borrow::Cow;
4use alloc::boxed::Box;
5use core::num::NonZeroU32;
6
7pub trait TransactionManager<Conn: Connection> {
12 type TransactionStateData;
15
16 fn begin_transaction(conn: &mut Conn) -> QueryResult<()>;
22
23 fn rollback_transaction(conn: &mut Conn) -> QueryResult<()>;
29
30 fn commit_transaction(conn: &mut Conn) -> QueryResult<()>;
36
37 #[diesel_derives::__diesel_public_if(
43 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
44 )]
45 fn transaction_manager_status_mut(conn: &mut Conn) -> &mut TransactionManagerStatus;
46
47 fn transaction<F, R, E>(conn: &mut Conn, callback: F) -> Result<R, E>
52 where
53 F: FnOnce(&mut Conn) -> Result<R, E>,
54 E: From<Error>,
55 {
56 Self::begin_transaction(conn)?;
57 match callback(&mut *conn) {
58 Ok(value) => {
59 Self::commit_transaction(conn)?;
60 Ok(value)
61 }
62 Err(user_error) => match Self::rollback_transaction(conn) {
63 Ok(()) => Err(user_error),
64 Err(Error::BrokenTransactionManager) => {
65 Err(user_error)
68 }
69 Err(rollback_error) => Err(rollback_error.into()),
70 },
71 }
72 }
73
74 #[diesel_derives::__diesel_public_if(
82 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
83 )]
84 fn is_broken_transaction_manager(conn: &mut Conn) -> bool {
85 match Self::transaction_manager_status_mut(conn).transaction_state() {
86 Ok(ValidTransactionManagerStatus {
89 in_transaction: None,
90 }) => false,
91 Err(_) => true,
94 Ok(ValidTransactionManagerStatus {
98 in_transaction: Some(s),
99 }) => !s.test_transaction,
100 }
101 }
102}
103
104#[derive(#[automatically_derived]
impl ::core::default::Default for AnsiTransactionManager {
#[inline]
fn default() -> Self {
AnsiTransactionManager { status: ::core::default::Default::default() }
}
}Default, #[automatically_derived]
impl ::core::fmt::Debug for AnsiTransactionManager {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field1_finish(f,
"AnsiTransactionManager", "status", &&self.status)
}
}Debug)]
107pub struct AnsiTransactionManager {
108 pub(crate) status: TransactionManagerStatus,
109}
110
111#[doc = " Status of the transaction manager"]
pub enum TransactionManagerStatus {
Valid(ValidTransactionManagerStatus),
InError,
}#[diesel_derives::__diesel_public_if(
113 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
114)]
115#[derive(#[automatically_derived]
impl ::core::fmt::Debug for TransactionManagerStatus {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Self::Valid(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Valid",
&__self_0),
Self::InError => ::core::fmt::Formatter::write_str(f, "InError"),
}
}
}Debug)]
116pub enum TransactionManagerStatus {
117 Valid(ValidTransactionManagerStatus),
119 InError,
121}
122
123impl Default for TransactionManagerStatus {
124 fn default() -> Self {
125 TransactionManagerStatus::Valid(ValidTransactionManagerStatus::default())
126 }
127}
128
129impl TransactionManagerStatus {
130 pub fn transaction_depth(&self) -> QueryResult<Option<NonZeroU32>> {
133 match self {
134 TransactionManagerStatus::Valid(valid_status) => Ok(valid_status.transaction_depth()),
135 TransactionManagerStatus::InError => Err(Error::BrokenTransactionManager),
136 }
137 }
138
139 #[cfg(any(
140 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
141 feature = "postgres",
142 feature = "mysql",
143 feature = "mariadb"
144 ))]
145 #[doc =
" If in transaction and transaction manager is not broken, registers that it\'s possible that"]
#[doc =
" the connection can not be used anymore until top-level transaction is rolled back."]
#[doc = ""]
#[doc =
" If that is registered, savepoints rollbacks will still be attempted, but failure to do so"]
#[doc = " will not result in an error. (Some may succeed, some may not.)"]
pub fn set_requires_rollback_maybe_up_to_top_level(&mut self, to: bool) {
if let TransactionManagerStatus::Valid(ValidTransactionManagerStatus {
in_transaction: Some(InTransactionStatus {
requires_rollback_maybe_up_to_top_level, .. }) }) = self {
*requires_rollback_maybe_up_to_top_level = to;
}
}#[diesel_derives::__diesel_public_if(
146 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
147 )]
148 pub(crate) fn set_requires_rollback_maybe_up_to_top_level(&mut self, to: bool) {
154 if let TransactionManagerStatus::Valid(ValidTransactionManagerStatus {
155 in_transaction:
156 Some(InTransactionStatus {
157 requires_rollback_maybe_up_to_top_level,
158 ..
159 }),
160 }) = self
161 {
162 *requires_rollback_maybe_up_to_top_level = to;
163 }
164 }
165
166 pub fn set_in_error(&mut self) {
171 *self = TransactionManagerStatus::InError
172 }
173
174 #[doc = " Expose access to the inner transaction state"]
#[doc = ""]
#[doc =
" This function returns an error if the Transaction manager is in a broken"]
#[doc = " state"]
pub fn transaction_state(&mut self)
-> QueryResult<&mut ValidTransactionManagerStatus> {
match self {
TransactionManagerStatus::Valid(valid_status) => Ok(valid_status),
TransactionManagerStatus::InError =>
Err(Error::BrokenTransactionManager),
}
}#[diesel_derives::__diesel_public_if(
179 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
180 )]
181 pub(self) fn transaction_state(&mut self) -> QueryResult<&mut ValidTransactionManagerStatus> {
182 match self {
183 TransactionManagerStatus::Valid(valid_status) => Ok(valid_status),
184 TransactionManagerStatus::InError => Err(Error::BrokenTransactionManager),
185 }
186 }
187
188 #[doc = " This function allows to flag a transaction manager"]
#[doc = " in such a way that it contains a test transaction."]
#[doc = ""]
#[doc = " This will disable some checks in regards to open transactions"]
#[doc = " to allow `Connection::begin_test_transaction` to work with"]
#[doc = " pooled connections as well"]
pub fn set_test_transaction_flag(&mut self) {
if let TransactionManagerStatus::Valid(ValidTransactionManagerStatus {
in_transaction: Some(s) }) = self {
s.test_transaction = true;
}
}#[diesel_derives::__diesel_public_if(
195 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
196 )]
197 pub(crate) fn set_test_transaction_flag(&mut self) {
198 if let TransactionManagerStatus::Valid(ValidTransactionManagerStatus {
199 in_transaction: Some(s),
200 }) = self
201 {
202 s.test_transaction = true;
203 }
204 }
205}
206
207#[allow(missing_copy_implementations)]
209#[derive(#[automatically_derived]
#[allow(missing_copy_implementations)]
impl ::core::fmt::Debug for ValidTransactionManagerStatus {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field1_finish(f,
"ValidTransactionManagerStatus", "in_transaction",
&&self.in_transaction)
}
}Debug, #[automatically_derived]
#[allow(missing_copy_implementations)]
impl ::core::default::Default for ValidTransactionManagerStatus {
#[inline]
fn default() -> Self {
ValidTransactionManagerStatus {
in_transaction: ::core::default::Default::default(),
}
}
}Default)]
210#[doc =
" Valid transaction status for the manager. Can return the current transaction depth"]
#[allow(missing_copy_implementations)]
#[non_exhaustive]
pub struct ValidTransactionManagerStatus {
#[doc = " Inner status, or `None` if no transaction is running"]
pub in_transaction: Option<InTransactionStatus>,
}#[diesel_derives::__diesel_public_if(
211 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
212 public_fields(in_transaction)
213)]
214pub struct ValidTransactionManagerStatus {
215 in_transaction: Option<InTransactionStatus>,
217}
218
219#[allow(missing_copy_implementations)]
222#[derive(#[automatically_derived]
#[allow(missing_copy_implementations)]
impl ::core::fmt::Debug for InTransactionStatus {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field3_finish(f,
"InTransactionStatus", "transaction_depth",
&self.transaction_depth,
"requires_rollback_maybe_up_to_top_level",
&self.requires_rollback_maybe_up_to_top_level, "test_transaction",
&&self.test_transaction)
}
}Debug)]
223#[doc = " Various status fields to track the status of"]
#[doc = " a transaction manager with a started transaction"]
#[allow(missing_copy_implementations)]
#[non_exhaustive]
pub struct InTransactionStatus {
#[doc = " The current depth of nested transactions"]
pub transaction_depth: NonZeroU32,
#[doc =
" If that is registered, savepoints rollbacks will still be attempted, but failure to do so"]
#[doc = " will not result in an error. (Some may succeed, some may not.)"]
pub requires_rollback_maybe_up_to_top_level: bool,
#[doc = " Is this transaction manager status marked as test-transaction?"]
pub test_transaction: bool,
}#[diesel_derives::__diesel_public_if(
224 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
225 public_fields(
226 test_transaction,
227 transaction_depth,
228 requires_rollback_maybe_up_to_top_level
229 )
230)]
231pub struct InTransactionStatus {
232 transaction_depth: NonZeroU32,
234 requires_rollback_maybe_up_to_top_level: bool,
237 test_transaction: bool,
239}
240
241impl ValidTransactionManagerStatus {
242 pub fn transaction_depth(&self) -> Option<NonZeroU32> {
247 self.in_transaction.as_ref().map(|it| it.transaction_depth)
248 }
249
250 pub fn change_transaction_depth(
253 &mut self,
254 transaction_depth_change: TransactionDepthChange,
255 ) -> QueryResult<()> {
256 match (&mut self.in_transaction, transaction_depth_change) {
257 (Some(in_transaction), TransactionDepthChange::IncreaseDepth) => {
258 in_transaction.transaction_depth =
261 NonZeroU32::new(in_transaction.transaction_depth.get().saturating_add(1))
262 .expect("nz + nz is always non-zero");
263 Ok(())
264 }
265 (Some(in_transaction), TransactionDepthChange::DecreaseDepth) => {
266 match NonZeroU32::new(in_transaction.transaction_depth.get() - 1) {
268 Some(depth) => in_transaction.transaction_depth = depth,
269 None => self.in_transaction = None,
270 }
271 Ok(())
272 }
273 (None, TransactionDepthChange::IncreaseDepth) => {
274 self.in_transaction = Some(InTransactionStatus {
275 transaction_depth: NonZeroU32::new(1).expect("1 is non-zero"),
276 requires_rollback_maybe_up_to_top_level: false,
277 test_transaction: false,
278 });
279 Ok(())
280 }
281 (None, TransactionDepthChange::DecreaseDepth) => {
282 Err(Error::NotInTransaction)
286 }
287 }
288 }
289}
290
291#[derive(#[automatically_derived]
impl ::core::fmt::Debug for TransactionDepthChange {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
TransactionDepthChange::IncreaseDepth => "IncreaseDepth",
TransactionDepthChange::DecreaseDepth => "DecreaseDepth",
})
}
}Debug, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for TransactionDepthChange { }
#[automatically_derived]
impl ::core::clone::Clone for TransactionDepthChange {
#[inline]
fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for TransactionDepthChange { }Copy)]
293pub enum TransactionDepthChange {
294 IncreaseDepth,
296 DecreaseDepth,
298}
299
300impl AnsiTransactionManager {
301 fn get_transaction_state<Conn>(
302 conn: &mut Conn,
303 ) -> QueryResult<&mut ValidTransactionManagerStatus>
304 where
305 Conn: Connection<TransactionManager = Self>,
306 {
307 conn.transaction_state().status.transaction_state()
308 }
309
310 pub fn begin_transaction_sql<Conn>(conn: &mut Conn, sql: &str) -> QueryResult<()>
316 where
317 Conn: Connection<TransactionManager = Self>,
318 {
319 let state = Self::get_transaction_state(conn)?;
320 if let Some(_depth) = state.transaction_depth() {
321 return Err(Error::AlreadyInTransaction);
322 }
323 let instrumentation_depth = NonZeroU32::new(1);
324 conn.instrumentation().on_connection_event(
327 super::instrumentation::InstrumentationEvent::BeginTransaction {
328 depth: instrumentation_depth.expect("We know that 1 is not zero"),
329 },
330 );
331 conn.batch_execute(sql)?;
332 Self::get_transaction_state(conn)?
333 .change_transaction_depth(TransactionDepthChange::IncreaseDepth)?;
334
335 Ok(())
336 }
337}
338
339impl<Conn> TransactionManager<Conn> for AnsiTransactionManager
340where
341 Conn: Connection<TransactionManager = Self>,
342{
343 type TransactionStateData = Self;
344
345 fn begin_transaction(conn: &mut Conn) -> QueryResult<()> {
346 let transaction_state = Self::get_transaction_state(conn)?;
347 let transaction_depth = transaction_state.transaction_depth();
348 let start_transaction_sql = match transaction_depth {
349 None => Cow::from("BEGIN"),
350 Some(transaction_depth) => Cow::from(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("SAVEPOINT diesel_savepoint_{0}",
transaction_depth))
})alloc::format!(
351 "SAVEPOINT diesel_savepoint_{transaction_depth}"
352 )),
353 };
354 let instrumentation_depth =
355 NonZeroU32::new(transaction_depth.map_or(0, NonZeroU32::get).wrapping_add(1));
356 let sql = &start_transaction_sql;
357 conn.instrumentation().on_connection_event(
360 super::instrumentation::InstrumentationEvent::BeginTransaction {
361 depth: instrumentation_depth.expect("Transaction depth is too large"),
362 },
363 );
364 conn.batch_execute(sql)?;
365 Self::get_transaction_state(conn)?
366 .change_transaction_depth(TransactionDepthChange::IncreaseDepth)?;
367
368 Ok(())
369 }
370
371 fn rollback_transaction(conn: &mut Conn) -> QueryResult<()> {
372 let transaction_state = Self::get_transaction_state(conn)?;
373
374 let (
375 (rollback_sql, rolling_back_top_level),
376 requires_rollback_maybe_up_to_top_level_before_execute,
377 ) = match transaction_state.in_transaction {
378 Some(ref in_transaction) => (
379 match in_transaction.transaction_depth.get() {
380 1 => (Cow::Borrowed("ROLLBACK"), true),
381 depth_gt1 => (
382 Cow::Owned(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("ROLLBACK TO SAVEPOINT diesel_savepoint_{0}",
depth_gt1 - 1))
})alloc::format!(
383 "ROLLBACK TO SAVEPOINT diesel_savepoint_{}",
384 depth_gt1 - 1
385 )),
386 false,
387 ),
388 },
389 in_transaction.requires_rollback_maybe_up_to_top_level,
390 ),
391 None => return Err(Error::NotInTransaction),
392 };
393 let depth = transaction_state
394 .transaction_depth()
395 .expect("We know that we are in a transaction here");
396 conn.instrumentation().on_connection_event(
397 super::instrumentation::InstrumentationEvent::RollbackTransaction { depth },
398 );
399
400 match conn.batch_execute(&rollback_sql) {
401 Ok(()) => {
402 match Self::get_transaction_state(conn)?
403 .change_transaction_depth(TransactionDepthChange::DecreaseDepth)
404 {
405 Ok(()) => {}
406 Err(Error::NotInTransaction) if rolling_back_top_level => {
407 }
410 Err(e) => return Err(e),
411 }
412 Ok(())
413 }
414 Err(rollback_error) => {
415 let tm_status = Self::transaction_manager_status_mut(conn);
416 match tm_status {
417 TransactionManagerStatus::Valid(ValidTransactionManagerStatus {
418 in_transaction:
419 Some(InTransactionStatus {
420 transaction_depth,
421 requires_rollback_maybe_up_to_top_level,
422 ..
423 }),
424 }) if transaction_depth.get() > 1 => {
425 *transaction_depth = NonZeroU32::new(transaction_depth.get() - 1)
433 .expect("Depth was checked to be > 1");
434 *requires_rollback_maybe_up_to_top_level = true;
435 if requires_rollback_maybe_up_to_top_level_before_execute {
436 return Ok(());
439 }
440 }
441 TransactionManagerStatus::Valid(ValidTransactionManagerStatus {
442 in_transaction: None,
443 }) => {
444 }
449 _ => tm_status.set_in_error(),
450 }
451 Err(rollback_error)
452 }
453 }
454 }
455
456 fn commit_transaction(conn: &mut Conn) -> QueryResult<()> {
462 let transaction_state = Self::get_transaction_state(conn)?;
463 let transaction_depth = transaction_state.transaction_depth();
464 let (commit_sql, committing_top_level) = match transaction_depth {
465 None => return Err(Error::NotInTransaction),
466 Some(transaction_depth) if transaction_depth.get() == 1 => {
467 (Cow::Borrowed("COMMIT"), true)
468 }
469 Some(transaction_depth) => (
470 Cow::Owned(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("RELEASE SAVEPOINT diesel_savepoint_{0}",
transaction_depth.get() - 1))
})alloc::format!(
471 "RELEASE SAVEPOINT diesel_savepoint_{}",
472 transaction_depth.get() - 1
473 )),
474 false,
475 ),
476 };
477 let depth = transaction_state
478 .transaction_depth()
479 .expect("We know that we are in a transaction here");
480 conn.instrumentation().on_connection_event(
481 super::instrumentation::InstrumentationEvent::CommitTransaction { depth },
482 );
483 match conn.batch_execute(&commit_sql) {
484 Ok(()) => {
485 match Self::get_transaction_state(conn)?
486 .change_transaction_depth(TransactionDepthChange::DecreaseDepth)
487 {
488 Ok(()) => {}
489 Err(Error::NotInTransaction) if committing_top_level => {
490 }
493 Err(e) => return Err(e),
494 }
495 Ok(())
496 }
497 Err(commit_error) => {
498 if let TransactionManagerStatus::Valid(ValidTransactionManagerStatus {
499 in_transaction:
500 Some(InTransactionStatus {
501 requires_rollback_maybe_up_to_top_level,
502 ..
503 }),
504 }) = conn.transaction_state().status
505 && (committing_top_level || requires_rollback_maybe_up_to_top_level)
506 {
507 match Self::rollback_transaction(conn) {
508 Ok(()) => {}
509 Err(rollback_error) => {
510 conn.transaction_state().status.set_in_error();
511 return Err(Error::RollbackErrorOnCommit {
512 rollback_error: Box::new(rollback_error),
513 commit_error: Box::new(commit_error),
514 });
515 }
516 }
517 }
518 Err(commit_error)
519 }
520 }
521 }
522
523 fn transaction_manager_status_mut(conn: &mut Conn) -> &mut TransactionManagerStatus {
524 &mut conn.transaction_state().status
525 }
526}
527
528#[cfg(test)]
529#[allow(clippy::uninlined_format_args)]
531mod test {
532 mod mock {
534 use crate::connection::Instrumentation;
535 use crate::connection::transaction_manager::AnsiTransactionManager;
536 use crate::connection::{
537 Connection, ConnectionSealed, SimpleConnection, TransactionManager,
538 };
539 use crate::result::QueryResult;
540 use crate::test_helpers::TestConnection;
541 use std::collections::VecDeque;
542
543 pub(crate) struct MockConnection {
544 pub(crate) next_results: VecDeque<QueryResult<usize>>,
545 pub(crate) next_batch_execute_results: VecDeque<QueryResult<()>>,
546 transaction_state: AnsiTransactionManager,
547 instrumentation: Option<Box<dyn Instrumentation>>,
548 }
549
550 impl SimpleConnection for MockConnection {
551 fn batch_execute(&mut self, _query: &str) -> QueryResult<()> {
552 self.next_batch_execute_results
553 .pop_front()
554 .expect("No next result")
555 }
556 }
557
558 impl ConnectionSealed for MockConnection {}
559
560 impl Connection for MockConnection {
561 type Backend = <TestConnection as Connection>::Backend;
562
563 type TransactionManager = AnsiTransactionManager;
564
565 fn establish(_database_url: &str) -> crate::ConnectionResult<Self> {
566 Ok(Self {
567 next_results: VecDeque::new(),
568 next_batch_execute_results: VecDeque::new(),
569 transaction_state: AnsiTransactionManager::default(),
570 instrumentation: None,
571 })
572 }
573
574 fn execute_returning_count<T>(&mut self, _source: &T) -> QueryResult<usize>
575 where
576 T: crate::query_builder::QueryFragment<Self::Backend>
577 + crate::query_builder::QueryId,
578 {
579 self.next_results.pop_front().expect("No next result")
580 }
581
582 fn transaction_state(
583 &mut self,
584 ) -> &mut <Self::TransactionManager as TransactionManager<Self>>::TransactionStateData
585 {
586 &mut self.transaction_state
587 }
588
589 fn instrumentation(&mut self) -> &mut dyn crate::connection::Instrumentation {
590 &mut self.instrumentation
591 }
592
593 fn set_instrumentation(
594 &mut self,
595 instrumentation: impl crate::connection::Instrumentation,
596 ) {
597 self.instrumentation = Some(Box::new(instrumentation));
598 }
599
600 fn set_prepared_statement_cache_size(&mut self, _size: crate::connection::CacheSize) {
601 panic!("implement, if you want to use it")
602 }
603 }
604 }
605
606 #[diesel_test_helper::test]
607 #[cfg(feature = "postgres")]
608 fn transaction_manager_returns_an_error_when_attempting_to_commit_outside_of_a_transaction() {
609 use crate::PgConnection;
610 use crate::connection::transaction_manager::AnsiTransactionManager;
611 use crate::connection::transaction_manager::TransactionManager;
612 use crate::result::Error;
613
614 let conn = &mut crate::test_helpers::pg_connection_no_transaction();
615 assert_eq!(
616 None,
617 <AnsiTransactionManager as TransactionManager<PgConnection>>::transaction_manager_status_mut(
618 conn
619 ).transaction_depth().expect("Transaction depth")
620 );
621 let result = AnsiTransactionManager::commit_transaction(conn);
622 assert!(matches!(result, Err(Error::NotInTransaction)))
623 }
624
625 #[diesel_test_helper::test]
626 #[cfg(feature = "postgres")]
627 fn transaction_manager_returns_an_error_when_attempting_to_rollback_outside_of_a_transaction() {
628 use crate::PgConnection;
629 use crate::connection::transaction_manager::AnsiTransactionManager;
630 use crate::connection::transaction_manager::TransactionManager;
631 use crate::result::Error;
632
633 let conn = &mut crate::test_helpers::pg_connection_no_transaction();
634 assert_eq!(
635 None,
636 <AnsiTransactionManager as TransactionManager<PgConnection>>::transaction_manager_status_mut(
637 conn
638 ).transaction_depth().expect("Transaction depth")
639 );
640 let result = AnsiTransactionManager::rollback_transaction(conn);
641 assert!(matches!(result, Err(Error::NotInTransaction)))
642 }
643
644 #[diesel_test_helper::test]
645 fn transaction_manager_enters_broken_state_when_connection_is_broken() {
646 use crate::connection::TransactionManagerStatus;
647 use crate::connection::transaction_manager::AnsiTransactionManager;
648 use crate::connection::transaction_manager::TransactionManager;
649 use crate::result::{DatabaseErrorKind, Error};
650 use crate::*;
651
652 let mut conn = mock::MockConnection::establish("mock").expect("Mock connection");
653
654 conn.next_batch_execute_results.push_back(Ok(()));
656 let result = conn.transaction(|conn| {
657 conn.next_results.push_back(Ok(1));
658 let query_result = sql_query("SELECT 1").execute(conn);
659 assert!(query_result.is_ok());
660 conn.next_batch_execute_results
662 .push_back(Err(Error::DatabaseError(
663 DatabaseErrorKind::Unknown,
664 Box::new("commit fails".to_string()),
665 )));
666 conn.next_batch_execute_results
667 .push_back(Err(Error::DatabaseError(
668 DatabaseErrorKind::Unknown,
669 Box::new("rollback also fails".to_string()),
670 )));
671 Ok(())
672 });
673 assert!(
674 matches!(
675 &result,
676 Err(Error::RollbackErrorOnCommit {
677 rollback_error,
678 commit_error
679 }) if matches!(**commit_error, Error::DatabaseError(DatabaseErrorKind::Unknown, _))
680 && matches!(&**rollback_error,
681 Error::DatabaseError(DatabaseErrorKind::Unknown, msg)
682 if msg.message() == "rollback also fails"
683 )
684 ),
685 "Got {:?}",
686 result
687 );
688 assert!(matches!(
689 *AnsiTransactionManager::transaction_manager_status_mut(&mut conn),
690 TransactionManagerStatus::InError
691 ));
692 let result = conn.transaction(|_conn| Ok(()));
694 assert!(matches!(result, Err(Error::BrokenTransactionManager)))
695 }
696
697 #[diesel_test_helper::test]
698 fn failed_top_level_commit_is_rolled_back() {
699 use crate::connection::transaction_manager::AnsiTransactionManager;
700 use crate::connection::transaction_manager::TransactionManager;
701 use crate::result::{DatabaseErrorKind, Error};
702 use crate::*;
703
704 let mut conn = mock::MockConnection::establish("mock").expect("Mock connection");
705 conn.next_batch_execute_results.extend([
706 Ok(()),
707 Err(Error::DatabaseError(
708 DatabaseErrorKind::ForeignKeyViolation,
709 Box::new("commit fails".to_string()),
710 )),
711 Ok(()),
712 ]);
713 let result = conn.transaction(|_| Ok(()));
714 assert!(
715 matches!(
716 result,
717 Err(Error::DatabaseError(
718 DatabaseErrorKind::ForeignKeyViolation,
719 _
720 ))
721 ),
722 "{result:?}"
723 );
724 assert!(conn.next_batch_execute_results.is_empty());
725 assert_eq!(
726 Ok(None),
727 AnsiTransactionManager::transaction_manager_status_mut(&mut conn).transaction_depth()
728 );
729 }
730
731 #[diesel_test_helper::test]
732 fn failed_savepoint_release_is_not_rolled_back() {
733 use crate::connection::transaction_manager::AnsiTransactionManager;
734 use crate::connection::transaction_manager::TransactionManager;
735 use crate::result::{DatabaseErrorKind, Error};
736 use crate::*;
737 use core::num::NonZeroU32;
738
739 let mut conn = mock::MockConnection::establish("mock").expect("Mock connection");
740 conn.next_batch_execute_results.extend([
741 Ok(()),
742 Ok(()),
743 Err(Error::DatabaseError(
744 DatabaseErrorKind::Unknown,
745 Box::new("release fails".to_string()),
746 )),
747 ]);
748 AnsiTransactionManager::begin_transaction(&mut conn).unwrap();
749 AnsiTransactionManager::begin_transaction(&mut conn).unwrap();
750 assert!(AnsiTransactionManager::commit_transaction(&mut conn).is_err());
751 assert!(conn.next_batch_execute_results.is_empty());
752 assert_eq!(
753 Ok(NonZeroU32::new(2)),
754 AnsiTransactionManager::transaction_manager_status_mut(&mut conn).transaction_depth()
755 );
756 }
757
758 #[diesel_test_helper::test]
759 #[cfg(feature = "mysql")]
760 fn mysql_transaction_is_rolled_back_upon_syntax_error() {
761 use crate::connection::transaction_manager::AnsiTransactionManager;
762 use crate::connection::transaction_manager::TransactionManager;
763 use crate::*;
764 use std::num::NonZeroU32;
765
766 let conn = &mut crate::test_helpers::connection_no_transaction();
767 assert_eq!(
768 None,
769 <AnsiTransactionManager as TransactionManager<MysqlConnection>>::transaction_manager_status_mut(
770 conn
771 ).transaction_depth().expect("Transaction depth")
772 );
773 let _result = conn.transaction(|conn| {
774 assert_eq!(
775 NonZeroU32::new(1),
776 <AnsiTransactionManager as TransactionManager<MysqlConnection>>::transaction_manager_status_mut(
777 conn
778 ).transaction_depth().expect("Transaction depth")
779 );
780 let query_result = sql_query("SELECT_SYNTAX_ERROR 1").execute(conn);
782 assert!(query_result.is_err());
783 query_result
784 });
785 assert_eq!(
786 None,
787 <AnsiTransactionManager as TransactionManager<MysqlConnection>>::transaction_manager_status_mut(
788 conn
789 ).transaction_depth().expect("Transaction depth")
790 );
791 }
792
793 #[diesel_test_helper::test]
794 #[cfg(not(miri))] #[cfg(feature = "__sqlite-shared")]
799 fn sqlite_transaction_is_rolled_back_upon_syntax_error() {
800 use crate::connection::transaction_manager::AnsiTransactionManager;
801 use crate::connection::transaction_manager::TransactionManager;
802 use crate::*;
803 use std::num::NonZeroU32;
804
805 let conn = &mut crate::test_helpers::connection();
806 assert_eq!(
807 None,
808 <AnsiTransactionManager as TransactionManager<SqliteConnection>>::transaction_manager_status_mut(
809 conn
810 ).transaction_depth().expect("Transaction depth")
811 );
812 let _result = conn.transaction(|conn| {
813 assert_eq!(
814 NonZeroU32::new(1),
815 <AnsiTransactionManager as TransactionManager<SqliteConnection>>::transaction_manager_status_mut(
816 conn
817 ).transaction_depth().expect("Transaction depth")
818 );
819 let query_result = sql_query("SELECT_SYNTAX_ERROR 1").execute(conn);
821 assert!(query_result.is_err());
822 query_result
823 });
824 assert_eq!(
825 None,
826 <AnsiTransactionManager as TransactionManager<SqliteConnection>>::transaction_manager_status_mut(
827 conn
828 ).transaction_depth().expect("Transaction depth")
829 );
830 }
831
832 #[diesel_test_helper::test]
833 #[cfg(feature = "mysql")]
834 fn nested_mysql_transaction_is_rolled_back_upon_syntax_error() {
835 use crate::connection::transaction_manager::AnsiTransactionManager;
836 use crate::connection::transaction_manager::TransactionManager;
837 use crate::*;
838 use std::num::NonZeroU32;
839
840 let conn = &mut crate::test_helpers::connection_no_transaction();
841 assert_eq!(
842 None,
843 <AnsiTransactionManager as TransactionManager<MysqlConnection>>::transaction_manager_status_mut(
844 conn
845 ).transaction_depth().expect("Transaction depth")
846 );
847 let result = conn.transaction(|conn| {
848 assert_eq!(
849 NonZeroU32::new(1),
850 <AnsiTransactionManager as TransactionManager<MysqlConnection>>::transaction_manager_status_mut(
851 conn
852 ).transaction_depth().expect("Transaction depth")
853 );
854 let result = conn.transaction(|conn| {
855 assert_eq!(
856 NonZeroU32::new(2),
857 <AnsiTransactionManager as TransactionManager<MysqlConnection>>::transaction_manager_status_mut(
858 conn
859 ).transaction_depth().expect("Transaction depth")
860 );
861 sql_query("SELECT_SYNTAX_ERROR 1").execute(conn)
863 });
864 assert!(result.is_err());
865 assert_eq!(
866 NonZeroU32::new(1),
867 <AnsiTransactionManager as TransactionManager<MysqlConnection>>::transaction_manager_status_mut(
868 conn
869 ).transaction_depth().expect("Transaction depth")
870 );
871 let query_result = sql_query("SELECT 1").execute(conn);
872 assert!(query_result.is_ok());
873 query_result
874 });
875 assert!(result.is_ok());
876 assert_eq!(
877 None,
878 <AnsiTransactionManager as TransactionManager<MysqlConnection>>::transaction_manager_status_mut(
879 conn
880 ).transaction_depth().expect("Transaction depth")
881 );
882 }
883
884 #[diesel_test_helper::test]
885 #[cfg(feature = "mysql")]
886 #[allow(clippy::needless_collect)]
889 fn mysql_transaction_depth_commits_tracked_properly_on_serialization_failure() {
890 use crate::result::DatabaseErrorKind::SerializationFailure;
891 use crate::result::Error::DatabaseError;
892 use crate::*;
893 use std::num::NonZeroU32;
894 use std::sync::{Arc, Barrier};
895 use std::thread;
896
897 table! {
898 #[sql_name = "mysql_transaction_depth_is_tracked_properly_on_commit_failure"]
899 serialization_example {
900 id -> Integer,
901 class -> Integer,
902 }
903 }
904
905 let conn = &mut crate::test_helpers::connection_no_transaction();
906
907 sql_query(
908 "DROP TABLE IF EXISTS mysql_transaction_depth_is_tracked_properly_on_commit_failure;",
909 )
910 .execute(conn)
911 .unwrap();
912 sql_query(
913 r#"
914 CREATE TABLE mysql_transaction_depth_is_tracked_properly_on_commit_failure (
915 id INT AUTO_INCREMENT PRIMARY KEY,
916 class INTEGER NOT NULL
917 )
918 "#,
919 )
920 .execute(conn)
921 .unwrap();
922
923 insert_into(serialization_example::table)
924 .values(&vec![
925 serialization_example::class.eq(1),
926 serialization_example::class.eq(2),
927 ])
928 .execute(conn)
929 .unwrap();
930
931 let before_barrier = Arc::new(Barrier::new(2));
932 let after_barrier = Arc::new(Barrier::new(2));
933
934 let threads = (1..3)
935 .map(|i| {
936 let before_barrier = before_barrier.clone();
937 let after_barrier = after_barrier.clone();
938 thread::spawn(move || {
939 use crate::connection::transaction_manager::AnsiTransactionManager;
940 use crate::connection::transaction_manager::TransactionManager;
941 let conn = &mut crate::test_helpers::connection_no_transaction();
942 assert_eq!(None, <AnsiTransactionManager as TransactionManager<MysqlConnection>>::transaction_manager_status_mut(conn).transaction_depth().expect("Transaction depth"));
943 crate::sql_query("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE").execute(conn)?;
944
945 let result =
946 conn.transaction(|conn| {
947 assert_eq!(NonZeroU32::new(1), <AnsiTransactionManager as TransactionManager<MysqlConnection>>::transaction_manager_status_mut(conn).transaction_depth().expect("Transaction depth"));
948 let _ = serialization_example::table
949 .filter(serialization_example::class.eq(i))
950 .count()
951 .execute(conn)?;
952
953 let other_i = if i == 1 { 2 } else { 1 };
954 let q = insert_into(serialization_example::table)
955 .values(serialization_example::class.eq(other_i));
956 before_barrier.wait();
957
958 let r = q.execute(conn);
959 after_barrier.wait();
960 r
961 });
962
963 assert_eq!(None, <AnsiTransactionManager as TransactionManager<MysqlConnection>>::transaction_manager_status_mut(conn).transaction_depth().expect("Transaction depth"));
964
965 let second_trans_result = conn.transaction(|conn| crate::sql_query("SELECT 1").execute(conn));
966 assert!(second_trans_result.is_ok(), "Expected the thread connections to have been rolled back or committed, but second transaction exited with {:?}", second_trans_result);
967 result
968 })
969 })
970 .collect::<Vec<_>>();
971 let second_trans_result =
972 conn.transaction(|conn| crate::sql_query("SELECT 1").execute(conn));
973 assert!(
974 second_trans_result.is_ok(),
975 "Expected the main connection to have been rolled back or committed, but second transaction exited with {:?}",
976 second_trans_result
977 );
978
979 let mut results = threads
980 .into_iter()
981 .map(|t| t.join().unwrap())
982 .collect::<Vec<_>>();
983
984 results.sort_by_key(|r| r.is_err());
985 assert!(results[0].is_ok(), "Got {:?} instead", results);
986 assert!(
988 matches!(&results[1], Err(DatabaseError(SerializationFailure, _))),
989 "Got {:?} instead",
990 results
991 );
992 }
993
994 #[diesel_test_helper::test]
995 #[cfg(feature = "mysql")]
996 #[allow(clippy::needless_collect)]
999 fn mysql_nested_transaction_depth_commits_tracked_properly_on_serialization_failure() {
1000 use crate::result::DatabaseErrorKind::SerializationFailure;
1001 use crate::result::Error::DatabaseError;
1002 use crate::*;
1003 use std::num::NonZeroU32;
1004 use std::sync::{Arc, Barrier};
1005 use std::thread;
1006
1007 table! {
1008 #[sql_name = "mysql_nested_trans_depth_is_tracked_properly_on_commit_failure"]
1009 serialization_example {
1010 id -> Integer,
1011 class -> Integer,
1012 }
1013 }
1014
1015 let conn = &mut crate::test_helpers::connection_no_transaction();
1016
1017 sql_query(
1018 "DROP TABLE IF EXISTS mysql_nested_trans_depth_is_tracked_properly_on_commit_failure;",
1019 )
1020 .execute(conn)
1021 .unwrap();
1022 sql_query(
1023 r#"
1024 CREATE TABLE mysql_nested_trans_depth_is_tracked_properly_on_commit_failure (
1025 id INT AUTO_INCREMENT PRIMARY KEY,
1026 class INTEGER NOT NULL
1027 )
1028 "#,
1029 )
1030 .execute(conn)
1031 .unwrap();
1032
1033 insert_into(serialization_example::table)
1034 .values(&vec![
1035 serialization_example::class.eq(1),
1036 serialization_example::class.eq(2),
1037 ])
1038 .execute(conn)
1039 .unwrap();
1040
1041 let before_barrier = Arc::new(Barrier::new(2));
1042 let after_barrier = Arc::new(Barrier::new(2));
1043
1044 let threads = (1..3)
1045 .map(|i| {
1046 let before_barrier = before_barrier.clone();
1047 let after_barrier = after_barrier.clone();
1048 thread::spawn(move || {
1049 use crate::connection::transaction_manager::AnsiTransactionManager;
1050 use crate::connection::transaction_manager::TransactionManager;
1051 let conn = &mut crate::test_helpers::connection_no_transaction();
1052 assert_eq!(None, <AnsiTransactionManager as TransactionManager<MysqlConnection>>::transaction_manager_status_mut(conn).transaction_depth().expect("Transaction depth"));
1053 crate::sql_query("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE").execute(conn)?;
1054
1055 let result =
1056 conn.transaction(|conn| {
1057 assert_eq!(NonZeroU32::new(1), <AnsiTransactionManager as TransactionManager<MysqlConnection>>::transaction_manager_status_mut(conn).transaction_depth().expect("Transaction depth"));
1058 conn.transaction(|conn| {
1059 assert_eq!(NonZeroU32::new(2), <AnsiTransactionManager as TransactionManager<MysqlConnection>>::transaction_manager_status_mut(conn).transaction_depth().expect("Transaction depth"));
1060 let _ = serialization_example::table
1061 .filter(serialization_example::class.eq(i))
1062 .count()
1063 .execute(conn)?;
1064
1065 let other_i = if i == 1 { 2 } else { 1 };
1066 let q = insert_into(serialization_example::table)
1067 .values(serialization_example::class.eq(other_i));
1068 before_barrier.wait();
1069
1070 let r = q.execute(conn);
1071 after_barrier.wait();
1072 r
1073 })
1074 });
1075
1076 assert_eq!(None, <AnsiTransactionManager as TransactionManager<MysqlConnection>>::transaction_manager_status_mut(conn).transaction_depth().expect("Transaction depth"));
1077
1078 let second_trans_result = conn.transaction(|conn| crate::sql_query("SELECT 1").execute(conn));
1079 assert!(second_trans_result.is_ok(), "Expected the thread connections to have been rolled back or committed, but second transaction exited with {:?}", second_trans_result);
1080 result
1081 })
1082 })
1083 .collect::<Vec<_>>();
1084 let second_trans_result =
1085 conn.transaction(|conn| crate::sql_query("SELECT 1").execute(conn));
1086 assert!(
1087 second_trans_result.is_ok(),
1088 "Expected the main connection to have been rolled back or committed, but second transaction exited with {:?}",
1089 second_trans_result
1090 );
1091
1092 let mut results = threads
1093 .into_iter()
1094 .map(|t| t.join().unwrap())
1095 .collect::<Vec<_>>();
1096
1097 results.sort_by_key(|r| r.is_err());
1098 assert!(results[0].is_ok(), "Got {:?} instead", results);
1099 assert!(
1100 matches!(&results[1], Err(DatabaseError(SerializationFailure, _))),
1101 "Got {:?} instead",
1102 results
1103 );
1104 }
1105
1106 #[diesel_test_helper::test]
1107 #[cfg(feature = "__sqlite-shared")]
1108 #[cfg(not(miri))] #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
1113 fn sqlite_transaction_is_rolled_back_upon_deferred_constraint_failure() {
1114 use crate::connection::SimpleConnection;
1115 use crate::connection::transaction_manager::AnsiTransactionManager;
1116 use crate::connection::transaction_manager::TransactionManager;
1117 use crate::prelude::*;
1118 use crate::result::{DatabaseErrorKind, Error};
1119
1120 crate::table! {
1121 deferred_parents (id) {
1122 id -> Integer,
1123 }
1124 }
1125 crate::table! {
1126 deferred_children (id) {
1127 id -> Integer,
1128 parent -> Integer,
1129 }
1130 }
1131
1132 let dir = tempfile::tempdir().unwrap();
1133 let path = dir.path().join("deferred_fk.db");
1134 let url = path.to_str().unwrap();
1135 let conn = &mut SqliteConnection::establish(url).unwrap();
1136 conn.batch_execute(
1137 "PRAGMA foreign_keys = ON;
1138 CREATE TABLE deferred_parents (id INTEGER PRIMARY KEY);
1139 CREATE TABLE deferred_children (
1140 id INTEGER PRIMARY KEY,
1141 parent INTEGER NOT NULL
1142 REFERENCES deferred_parents(id) DEFERRABLE INITIALLY DEFERRED
1143 );",
1144 )
1145 .unwrap();
1146
1147 let refused = conn.transaction(|conn| {
1148 crate::insert_into(deferred_children::table)
1149 .values((deferred_children::id.eq(1), deferred_children::parent.eq(9)))
1150 .execute(conn)
1151 });
1152 assert!(
1153 matches!(
1154 refused,
1155 Err(Error::DatabaseError(
1156 DatabaseErrorKind::ForeignKeyViolation,
1157 _
1158 ))
1159 ),
1160 "{refused:?}"
1161 );
1162 assert_eq!(
1163 None,
1164 <AnsiTransactionManager as TransactionManager<SqliteConnection>>::transaction_manager_status_mut(
1165 conn
1166 ).transaction_depth().expect("Transaction depth")
1167 );
1168 assert_eq!(Ok(0_i64), deferred_children::table.count().get_result(conn));
1169
1170 conn.transaction(|conn| {
1171 crate::insert_into(deferred_parents::table)
1172 .values(deferred_parents::id.eq(5))
1173 .execute(conn)
1174 })
1175 .unwrap();
1176 let other = &mut SqliteConnection::establish(url).unwrap();
1177 assert_eq!(Ok(1_i64), deferred_parents::table.count().get_result(other));
1178 }
1179
1180 #[diesel_test_helper::test]
1181 #[cfg(not(miri))] #[cfg(feature = "__sqlite-shared")]
1186 fn sqlite_transaction_commits_after_a_recovered_statement_error() {
1187 use crate::connection::SimpleConnection;
1188 use crate::connection::transaction_manager::AnsiTransactionManager;
1189 use crate::connection::transaction_manager::TransactionManager;
1190 use crate::prelude::*;
1191 use crate::result::Error;
1192
1193 crate::table! {
1194 recovered_items (id) {
1195 id -> Integer,
1196 }
1197 }
1198
1199 let conn = &mut crate::test_helpers::connection();
1200 conn.batch_execute("CREATE TABLE recovered_items (id INTEGER PRIMARY KEY)")
1201 .unwrap();
1202 conn.transaction(|conn| {
1203 assert!(conn.batch_execute("SELECT_SYNTAX_ERROR 1").is_err());
1204 crate::insert_into(recovered_items::table)
1205 .values(recovered_items::id.eq(1))
1206 .execute(conn)?;
1207 Ok::<_, Error>(())
1208 })
1209 .unwrap();
1210 assert_eq!(
1211 None,
1212 <AnsiTransactionManager as TransactionManager<SqliteConnection>>::transaction_manager_status_mut(
1213 conn
1214 ).transaction_depth().expect("Transaction depth")
1215 );
1216 assert_eq!(Ok(1_i64), recovered_items::table.count().get_result(conn));
1217 }
1218
1219 #[diesel_test_helper::test]
1222 #[cfg(feature = "postgres")]
1223 fn some_libpq_failures_are_recoverable_by_rolling_back_the_savepoint_only() {
1224 use crate::connection::{AnsiTransactionManager, TransactionManager};
1225 use crate::prelude::*;
1226 use crate::sql_query;
1227
1228 crate::table! {
1229 rollback_test (id) {
1230 id -> Int4,
1231 value -> Int4,
1232 }
1233 }
1234
1235 let conn = &mut crate::test_helpers::pg_connection_no_transaction();
1236 assert_eq!(
1237 None,
1238 <AnsiTransactionManager as TransactionManager<PgConnection>>::transaction_manager_status_mut(
1239 conn
1240 ).transaction_depth().expect("Transaction depth")
1241 );
1242
1243 let res = conn.transaction(|conn| {
1244 sql_query(
1245 "CREATE TABLE IF NOT EXISTS rollback_test (id INT PRIMARY KEY, value INT NOT NULL)",
1246 )
1247 .execute(conn)?;
1248 conn.transaction(|conn| {
1249 sql_query("SET TRANSACTION READ ONLY").execute(conn)?;
1250 crate::update(rollback_test::table)
1251 .set(rollback_test::value.eq(0))
1252 .execute(conn)
1253 })
1254 .map(|_| {
1255 panic!("Should use the `or_else` branch");
1256 })
1257 .or_else(|_| sql_query("SELECT 1").execute(conn))
1258 .map(|_| ())
1259 });
1260 assert!(res.is_ok());
1261
1262 assert_eq!(
1263 None,
1264 <AnsiTransactionManager as TransactionManager<PgConnection>>::transaction_manager_status_mut(
1265 conn
1266 ).transaction_depth().expect("Transaction depth")
1267 );
1268 }
1269
1270 #[diesel_test_helper::test]
1271 #[cfg(feature = "postgres")]
1272 fn other_libpq_failures_are_not_recoverable_by_rolling_back_the_savepoint_only() {
1273 use crate::connection::{AnsiTransactionManager, TransactionManager};
1274 use crate::prelude::*;
1275 use crate::sql_query;
1276 use std::num::NonZeroU32;
1277 use std::sync::{Arc, Barrier};
1278
1279 crate::table! {
1280 rollback_test2 (id) {
1281 id -> Int4,
1282 value -> Int4,
1283 }
1284 }
1285 let conn = &mut crate::test_helpers::pg_connection_no_transaction();
1286
1287 sql_query(
1288 "CREATE TABLE IF NOT EXISTS rollback_test2 (id INT PRIMARY KEY, value INT NOT NULL)",
1289 )
1290 .execute(conn)
1291 .unwrap();
1292
1293 let start_barrier = Arc::new(Barrier::new(2));
1294 let commit_barrier = Arc::new(Barrier::new(2));
1295
1296 let other_start_barrier = start_barrier.clone();
1297 let other_commit_barrier = commit_barrier.clone();
1298
1299 let t1 = std::thread::spawn(move || {
1300 let conn = &mut crate::test_helpers::pg_connection_no_transaction();
1301 assert_eq!(
1302 None,
1303 <AnsiTransactionManager as TransactionManager<PgConnection>>::transaction_manager_status_mut(
1304 conn
1305 ).transaction_depth().expect("Transaction depth")
1306 );
1307 let r = conn.build_transaction().serializable().run::<_, crate::result::Error, _>(|conn| {
1308 assert_eq!(
1309 NonZeroU32::new(1),
1310 <AnsiTransactionManager as TransactionManager<PgConnection>>::transaction_manager_status_mut(
1311 conn
1312 ).transaction_depth().expect("Transaction depth")
1313 );
1314 rollback_test2::table.load::<(i32, i32)>(conn)?;
1315 crate::insert_into(rollback_test2::table)
1316 .values((rollback_test2::id.eq(1), rollback_test2::value.eq(42)))
1317 .execute(conn)?;
1318 let r = conn.transaction(|conn| {
1319 assert_eq!(
1320 NonZeroU32::new(2),
1321 <AnsiTransactionManager as TransactionManager<PgConnection>>::transaction_manager_status_mut(
1322 conn
1323 ).transaction_depth().expect("Transaction depth")
1324 );
1325 start_barrier.wait();
1326 commit_barrier.wait();
1327 let r = rollback_test2::table.load::<(i32, i32)>(conn);
1328 assert!(r.is_err());
1329 Err::<(), _>(crate::result::Error::RollbackTransaction)
1330 });
1331 assert_eq!(
1332 NonZeroU32::new(1),
1333 <AnsiTransactionManager as TransactionManager<PgConnection>>::transaction_manager_status_mut(
1334 conn
1335 ).transaction_depth().expect("Transaction depth")
1336 );
1337 assert!(
1338 matches!(r, Err(crate::result::Error::RollbackTransaction)),
1339 "rollback failed (such errors should be ignored by transaction manager): {}",
1340 r.unwrap_err()
1341 );
1342 let r = rollback_test2::table.load::<(i32, i32)>(conn);
1343 assert!(r.is_err());
1344 r
1348 });
1349 assert!(r.is_err());
1350 assert_eq!(
1351 None,
1352 <AnsiTransactionManager as TransactionManager<PgConnection>>::transaction_manager_status_mut(
1353 conn
1354 ).transaction_depth().expect("Transaction depth")
1355 );
1356 });
1357
1358 let t2 = std::thread::spawn(move || {
1359 other_start_barrier.wait();
1360 let conn = &mut crate::test_helpers::pg_connection_no_transaction();
1361 assert_eq!(
1362 None,
1363 <AnsiTransactionManager as TransactionManager<PgConnection>>::transaction_manager_status_mut(
1364 conn
1365 ).transaction_depth().expect("Transaction depth")
1366 );
1367 let r = conn.build_transaction().serializable().run::<_, crate::result::Error, _>(|conn| {
1368 assert_eq!(
1369 NonZeroU32::new(1),
1370 <AnsiTransactionManager as TransactionManager<PgConnection>>::transaction_manager_status_mut(
1371 conn
1372 ).transaction_depth().expect("Transaction depth")
1373 );
1374 let _ = rollback_test2::table.load::<(i32, i32)>(conn)?;
1375 crate::insert_into(rollback_test2::table)
1376 .values((rollback_test2::id.eq(23), rollback_test2::value.eq(42)))
1377 .execute(conn)?;
1378 Ok(())
1379 });
1380 other_commit_barrier.wait();
1381 assert!(r.is_ok(), "{:?}", r.unwrap_err());
1382 assert_eq!(
1383 None,
1384 <AnsiTransactionManager as TransactionManager<PgConnection>>::transaction_manager_status_mut(
1385 conn
1386 ).transaction_depth().expect("Transaction depth")
1387 );
1388 });
1389 crate::sql_query("DELETE FROM rollback_test2")
1390 .execute(conn)
1391 .unwrap();
1392 t1.join().unwrap();
1393 t2.join().unwrap();
1394 }
1395}