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