Remove useless try{} and rename some variables in ::TxnScopeGuard
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / TxnScopeGuard.pm
CommitLineData
6936e902 1package DBIx::Class::Storage::TxnScopeGuard;
1bc193ac 2
3use strict;
4use warnings;
3d56e026 5use Scalar::Util qw(weaken blessed refaddr);
b4ad8a74 6use DBIx::Class;
e1d9e578 7use DBIx::Class::_Util qw(is_exception detected_reinvoked_destructor);
70c28808 8use DBIx::Class::Carp;
9c1700e3 9use namespace::clean;
10
1bc193ac 11sub new {
12 my ($class, $storage) = @_;
13
f62c5724 14 my $guard = {
15 inactivated => 0,
16 storage => $storage,
17 };
18
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
6e102c8f 22 #
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...
35cf7d1a 26 #
27 # Deliberately *NOT* using is_exception - if someone left a misbehaving
28 # antipattern value in $@, it's not our business to whine about it
29 if( defined $@ and length $@ ) {
6e102c8f 30 weaken(
d52c4a75 31 $guard->{existing_exception_ref} = (length ref $@) ? $@ : \$@
6e102c8f 32 );
f62c5724 33 }
34
1bc193ac 35 $storage->txn_begin;
1f870d5a 36
6e102c8f 37 weaken( $guard->{dbh} = $storage->_dbh );
f62c5724 38
39 bless $guard, ref $class || $class;
1f870d5a 40
7d216b10 41 $guard;
1bc193ac 42}
43
44sub commit {
45 my $self = shift;
46
f62c5724 47 $self->{storage}->throw_exception("Refusing to execute multiple commits on scope guard $self")
48 if $self->{inactivated};
49
0bec44d5 50 # FIXME - this assumption may be premature: a commit may fail and a rollback
51 # *still* be necessary. Currently I am not aware of such scenarious, but I
52 # also know the deferred constraint handling is *severely* undertested.
53 # Making the change of "fire txn and never come back to this" in order to
54 # address RT#107159, but this *MUST* be reevaluated later.
f62c5724 55 $self->{inactivated} = 1;
0bec44d5 56 $self->{storage}->txn_commit;
1bc193ac 57}
58
59sub DESTROY {
e1d9e578 60 return if &detected_reinvoked_destructor;
3d56e026 61
85944825 62 return if $_[0]->{inactivated};
3b7f3eac 63
f62c5724 64 # if our dbh is not ours anymore, the $dbh weakref will go undef
85944825 65 $_[0]->{storage}->_verify_pid unless DBIx::Class::_ENV_::BROKEN_FORK;
66 return unless $_[0]->{dbh};
7d216b10 67
85944825 68 my $current_exception = (
841efcb3 69 is_exception $@
f62c5724 70 and
71 (
85944825 72 ! defined $_[0]->{existing_exception_ref}
f62c5724 73 or
85944825 74 refaddr( (length ref $@) ? $@ : \$@ ) != refaddr($_[0]->{existing_exception_ref})
f62c5724 75 )
85944825 76 )
77 ? $@
78 : undef
79 ;
c6e27318 80
a778f387 81 {
82 local $@;
36099e8c 83
84 carp 'A DBIx::Class::Storage::TxnScopeGuard went out of scope without explicit commit or error. Rolling back.'
85944825 85 unless defined $current_exception;
36099e8c 86
ed7ab0f4 87 my $rollback_exception;
7d216b10 88 # do minimal connectivity check due to weird shit like
89 # https://rt.cpan.org/Public/Bug/Display.html?id=62370
85944825 90 eval {
91 $_[0]->{storage}->_seems_connected && $_[0]->{storage}->txn_rollback;
92 1;
93 } or $rollback_exception = $@;
c6e27318 94
90d7422f 95 if ( $rollback_exception and (
96 ! defined blessed $rollback_exception
97 or
98 ! $rollback_exception->isa('DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION')
99 ) ) {
1f870d5a 100 # append our text - THIS IS A TEMPORARY FIXUP!
101 # a real stackable exception object is in the works
85944825 102 if (ref $current_exception eq 'DBIx::Class::Exception') {
103 $current_exception->{msg} = "Transaction aborted: $current_exception->{msg} "
1f870d5a 104 ."Rollback failed: ${rollback_exception}";
105 }
85944825 106 elsif ($current_exception) {
107 $current_exception = "Transaction aborted: ${current_exception} "
c6e27318 108 ."Rollback failed: ${rollback_exception}";
36099e8c 109 }
110 else {
111 carp (join ' ',
112 "********************* ROLLBACK FAILED!!! ********************",
113 "\nA rollback operation failed after the guard went out of scope.",
114 'This is potentially a disastrous situation, check your data for',
115 "consistency: $rollback_exception"
116 );
117 }
c6e27318 118 }
3b7f3eac 119 }
36099e8c 120
85944825 121 $@ = $current_exception;
1bc193ac 122}
123
1241;
125
126__END__
127
128=head1 NAME
129
6936e902 130DBIx::Class::Storage::TxnScopeGuard - Scope-based transaction handling
1bc193ac 131
132=head1 SYNOPSIS
133
134 sub foo {
135 my ($self, $schema) = @_;
136
137 my $guard = $schema->txn_scope_guard;
138
139 # Multiple database operations here
140
141 $guard->commit;
142 }
143
144=head1 DESCRIPTION
145
146An object that behaves much like L<Scope::Guard>, but hardcoded to do the
fd323bf1 147right thing with transactions in DBIx::Class.
1bc193ac 148
149=head1 METHODS
150
151=head2 new
152
6936e902 153Creating an instance of this class will start a new transaction (by
154implicitly calling L<DBIx::Class::Storage/txn_begin>. Expects a
1bc193ac 155L<DBIx::Class::Storage> object as its only argument.
156
157=head2 commit
158
159Commit the transaction, and stop guarding the scope. If this method is not
48580715 160called and this object goes out of scope (e.g. an exception is thrown) then
6936e902 161the transaction is rolled back, via L<DBIx::Class::Storage/txn_rollback>
1bc193ac 162
163=cut
164
165=head1 SEE ALSO
166
167L<DBIx::Class::Schema/txn_scope_guard>.
168
a2bd3796 169L<Scope::Guard> by chocolateboy (inspiration for this module)
1bc193ac 170
a2bd3796 171=head1 FURTHER QUESTIONS?
1bc193ac 172
a2bd3796 173Check the list of L<additional DBIC resources|DBIx::Class/GETTING HELP/SUPPORT>.
1bc193ac 174
a2bd3796 175=head1 COPYRIGHT AND LICENSE
1bc193ac 176
a2bd3796 177This module is free software L<copyright|DBIx::Class/COPYRIGHT AND LICENSE>
178by the L<DBIx::Class (DBIC) authors|DBIx::Class/AUTHORS>. You can
179redistribute it and/or modify it under the same terms as the
180L<DBIx::Class library|DBIx::Class/COPYRIGHT AND LICENSE>.