1 package DBIx::Class::Storage::TxnScopeGuard;
6 use Scalar::Util qw/weaken blessed refaddr/;
8 use DBIx::Class::_Util 'is_exception';
13 my ($class, $storage) = @_;
20 # we are starting with an already set $@ - in order for things to work we need to
21 # be able to recognize it upon destruction - store its weakref
22 # recording it before doing the txn_begin stuff
24 # FIXME FRAGILE - any eval that fails but *does not* rethrow between here
25 # and the unwind will trample over $@ and invalidate the entire mechanism
26 # There got to be a saner way of doing this...
27 if (is_exception $@) {
29 $guard->{existing_exception_ref} = (ref($@) eq '') ? \$@ : $@
35 weaken( $guard->{dbh} = $storage->_dbh );
37 bless $guard, ref $class || $class;
45 $self->{storage}->throw_exception("Refusing to execute multiple commits on scope guard $self")
46 if $self->{inactivated};
48 $self->{storage}->txn_commit;
49 $self->{inactivated} = 1;
55 return if $self->{inactivated};
57 # if our dbh is not ours anymore, the $dbh weakref will go undef
58 $self->{storage}->_verify_pid unless DBIx::Class::_ENV_::BROKEN_FORK;
59 return unless $self->{dbh};
61 my $exception = $@ if (
65 ! defined $self->{existing_exception_ref}
67 refaddr( ref($@) eq '' ? \$@ : $@ ) != refaddr($self->{existing_exception_ref})
74 carp 'A DBIx::Class::Storage::TxnScopeGuard went out of scope without explicit commit or error. Rolling back.'
75 unless defined $exception;
77 my $rollback_exception;
78 # do minimal connectivity check due to weird shit like
79 # https://rt.cpan.org/Public/Bug/Display.html?id=62370
80 try { $self->{storage}->_seems_connected && $self->{storage}->txn_rollback }
81 catch { $rollback_exception = shift };
83 if ( $rollback_exception and (
84 ! defined blessed $rollback_exception
86 ! $rollback_exception->isa('DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION')
88 # append our text - THIS IS A TEMPORARY FIXUP!
89 # a real stackable exception object is in the works
90 if (ref $exception eq 'DBIx::Class::Exception') {
91 $exception->{msg} = "Transaction aborted: $exception->{msg} "
92 ."Rollback failed: ${rollback_exception}";
95 $exception = "Transaction aborted: ${exception} "
96 ."Rollback failed: ${rollback_exception}";
100 "********************* ROLLBACK FAILED!!! ********************",
101 "\nA rollback operation failed after the guard went out of scope.",
102 'This is potentially a disastrous situation, check your data for',
103 "consistency: $rollback_exception"
118 DBIx::Class::Storage::TxnScopeGuard - Scope-based transaction handling
123 my ($self, $schema) = @_;
125 my $guard = $schema->txn_scope_guard;
127 # Multiple database operations here
134 An object that behaves much like L<Scope::Guard>, but hardcoded to do the
135 right thing with transactions in DBIx::Class.
141 Creating an instance of this class will start a new transaction (by
142 implicitly calling L<DBIx::Class::Storage/txn_begin>. Expects a
143 L<DBIx::Class::Storage> object as its only argument.
147 Commit the transaction, and stop guarding the scope. If this method is not
148 called and this object goes out of scope (e.g. an exception is thrown) then
149 the transaction is rolled back, via L<DBIx::Class::Storage/txn_rollback>
155 L<DBIx::Class::Schema/txn_scope_guard>.
161 Inspired by L<Scope::Guard> by chocolateboy.
163 This module is free software. It may be used, redistributed and/or modified
164 under the same terms as Perl itself.