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