Revert workarounds for $@ broken during 5.13.x - mainly 1f870d5a
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / TxnScopeGuard.pm
CommitLineData
6936e902 1package DBIx::Class::Storage::TxnScopeGuard;
1bc193ac 2
3use strict;
4use warnings;
ed7ab0f4 5use Try::Tiny;
f62c5724 6use Scalar::Util qw/weaken blessed refaddr/;
b4ad8a74 7use DBIx::Class;
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
22 if (defined $@ and $@ ne '') {
23 $guard->{existing_exception_ref} = (ref $@ ne '') ? $@ : \$@;
24 weaken $guard->{existing_exception_ref};
25 }
26
1bc193ac 27 $storage->txn_begin;
1f870d5a 28
f62c5724 29 $guard->{dbh} = $storage->_dbh;
30 weaken $guard->{dbh};
31
32 bless $guard, ref $class || $class;
1f870d5a 33
7d216b10 34 $guard;
1bc193ac 35}
36
37sub commit {
38 my $self = shift;
39
f62c5724 40 $self->{storage}->throw_exception("Refusing to execute multiple commits on scope guard $self")
41 if $self->{inactivated};
42
43 $self->{storage}->txn_commit;
44 $self->{inactivated} = 1;
1bc193ac 45}
46
47sub DESTROY {
f62c5724 48 my $self = shift;
1bc193ac 49
f62c5724 50 return if $self->{inactivated};
3b7f3eac 51
f62c5724 52 # if our dbh is not ours anymore, the $dbh weakref will go undef
0d8817bc 53 $self->{storage}->_verify_pid unless DBIx::Class::_ENV_::BROKEN_FORK;
f62c5724 54 return unless $self->{dbh};
7d216b10 55
f62c5724 56 my $exception = $@ if (
57 defined $@
58 and
59 $@ ne ''
60 and
61 (
62 ! defined $self->{existing_exception_ref}
63 or
64 refaddr( ref $@ eq '' ? \$@ : $@ ) != refaddr($self->{existing_exception_ref})
65 )
66 );
c6e27318 67
a778f387 68 {
69 local $@;
36099e8c 70
71 carp 'A DBIx::Class::Storage::TxnScopeGuard went out of scope without explicit commit or error. Rolling back.'
f62c5724 72 unless defined $exception;
36099e8c 73
ed7ab0f4 74 my $rollback_exception;
7d216b10 75 # do minimal connectivity check due to weird shit like
76 # https://rt.cpan.org/Public/Bug/Display.html?id=62370
f62c5724 77 try { $self->{storage}->_seems_connected && $self->{storage}->txn_rollback }
ed7ab0f4 78 catch { $rollback_exception = shift };
c6e27318 79
90d7422f 80 if ( $rollback_exception and (
81 ! defined blessed $rollback_exception
82 or
83 ! $rollback_exception->isa('DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION')
84 ) ) {
1f870d5a 85 # append our text - THIS IS A TEMPORARY FIXUP!
86 # a real stackable exception object is in the works
87 if (ref $exception eq 'DBIx::Class::Exception') {
88 $exception->{msg} = "Transaction aborted: $exception->{msg} "
89 ."Rollback failed: ${rollback_exception}";
90 }
91 elsif ($exception) {
36099e8c 92 $exception = "Transaction aborted: ${exception} "
c6e27318 93 ."Rollback failed: ${rollback_exception}";
36099e8c 94 }
95 else {
96 carp (join ' ',
97 "********************* ROLLBACK FAILED!!! ********************",
98 "\nA rollback operation failed after the guard went out of scope.",
99 'This is potentially a disastrous situation, check your data for',
100 "consistency: $rollback_exception"
101 );
102 }
c6e27318 103 }
3b7f3eac 104 }
36099e8c 105
5815ffb0 106 $@ = $exception;
1bc193ac 107}
108
1091;
110
111__END__
112
113=head1 NAME
114
6936e902 115DBIx::Class::Storage::TxnScopeGuard - Scope-based transaction handling
1bc193ac 116
117=head1 SYNOPSIS
118
119 sub foo {
120 my ($self, $schema) = @_;
121
122 my $guard = $schema->txn_scope_guard;
123
124 # Multiple database operations here
125
126 $guard->commit;
127 }
128
129=head1 DESCRIPTION
130
131An object that behaves much like L<Scope::Guard>, but hardcoded to do the
fd323bf1 132right thing with transactions in DBIx::Class.
1bc193ac 133
134=head1 METHODS
135
136=head2 new
137
6936e902 138Creating an instance of this class will start a new transaction (by
139implicitly calling L<DBIx::Class::Storage/txn_begin>. Expects a
1bc193ac 140L<DBIx::Class::Storage> object as its only argument.
141
142=head2 commit
143
144Commit the transaction, and stop guarding the scope. If this method is not
48580715 145called and this object goes out of scope (e.g. an exception is thrown) then
6936e902 146the transaction is rolled back, via L<DBIx::Class::Storage/txn_rollback>
1bc193ac 147
148=cut
149
150=head1 SEE ALSO
151
152L<DBIx::Class::Schema/txn_scope_guard>.
153
154=head1 AUTHOR
155
156Ash Berlin, 2008.
157
48580715 158Inspired by L<Scope::Guard> by chocolateboy.
1bc193ac 159
160This module is free software. It may be used, redistributed and/or modified
161under the same terms as Perl itself.
162
163=cut