Commit | Line | Data |
4012acd8 |
1 | package DBIx::Class::Storage; |
a62cf8d4 |
2 | |
3 | use strict; |
4 | use warnings; |
5 | |
4012acd8 |
6 | package # Hide from PAUSE |
7 | DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION; |
8 | |
9 | use overload '"' => sub { |
10 | 'DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION' |
11 | }; |
12 | |
13 | sub new { |
14 | my $class = shift; |
15 | my $self = {}; |
16 | return bless $self, $class; |
17 | } |
18 | |
19 | package DBIx::Class::Storage; |
20 | |
a62cf8d4 |
21 | sub new { die "Virtual method!" } |
82cc0386 |
22 | sub set_schema { die "Virtual method!" } |
a62cf8d4 |
23 | sub debug { die "Virtual method!" } |
24 | sub debugcb { die "Virtual method!" } |
25 | sub debugfh { die "Virtual method!" } |
96761eee |
26 | sub debugobj { die "Virtual method!" } |
27 | sub cursor { die "Virtual method!" } |
a62cf8d4 |
28 | sub disconnect { die "Virtual method!" } |
29 | sub connected { die "Virtual method!" } |
30 | sub ensure_connected { die "Virtual method!" } |
96761eee |
31 | sub on_connect_do { die "Virtual method!" } |
32 | sub connect_info { die "Virtual method!" } |
a62cf8d4 |
33 | sub sql_maker { die "Virtual method!" } |
34 | sub txn_begin { die "Virtual method!" } |
35 | sub txn_commit { die "Virtual method!" } |
36 | sub txn_rollback { die "Virtual method!" } |
37 | sub insert { die "Virtual method!" } |
38 | sub update { die "Virtual method!" } |
39 | sub delete { die "Virtual method!" } |
40 | sub select { die "Virtual method!" } |
41 | sub select_single { die "Virtual method!" } |
42 | sub columns_info_for { die "Virtual method!" } |
4012acd8 |
43 | sub throw_exception { die "Virtual method!" } |
a62cf8d4 |
44 | |
4012acd8 |
45 | =head2 txn_do |
a62cf8d4 |
46 | |
4012acd8 |
47 | =over 4 |
a62cf8d4 |
48 | |
4012acd8 |
49 | =item Arguments: C<$coderef>, @coderef_args? |
a62cf8d4 |
50 | |
4012acd8 |
51 | =item Return Value: The return value of $coderef |
52 | |
53 | =back |
54 | |
55 | Executes C<$coderef> with (optional) arguments C<@coderef_args> atomically, |
56 | returning its result (if any). If an exception is caught, a rollback is issued |
57 | and the exception is rethrown. If the rollback fails, (i.e. throws an |
58 | exception) an exception is thrown that includes a "Rollback failed" message. |
59 | |
60 | For example, |
61 | |
62 | my $author_rs = $schema->resultset('Author')->find(1); |
63 | my @titles = qw/Night Day It/; |
64 | |
65 | my $coderef = sub { |
66 | # If any one of these fails, the entire transaction fails |
67 | $author_rs->create_related('books', { |
68 | title => $_ |
69 | }) foreach (@titles); |
70 | |
71 | return $author->books; |
72 | }; |
73 | |
74 | my $rs; |
75 | eval { |
76 | $rs = $schema->txn_do($coderef); |
77 | }; |
78 | |
79 | if ($@) { # Transaction failed |
80 | die "something terrible has happened!" # |
81 | if ($@ =~ /Rollback failed/); # Rollback failed |
82 | |
83 | deal_with_failed_transaction(); |
84 | } |
85 | |
86 | In a nested transaction (calling txn_do() from within a txn_do() coderef) only |
87 | the outermost transaction will issue a L</txn_commit>, and txn_do() can be |
88 | called in void, scalar and list context and it will behave as expected. |
89 | |
90 | =cut |
91 | |
92 | sub txn_do { |
93 | my ($self, $coderef, @args) = @_; |
94 | |
95 | ref $coderef eq 'CODE' or $self->throw_exception |
96 | ('$coderef must be a CODE reference'); |
97 | |
98 | my (@return_values, $return_value); |
99 | |
100 | $self->txn_begin; # If this throws an exception, no rollback is needed |
101 | |
102 | my $wantarray = wantarray; # Need to save this since the context |
103 | # inside the eval{} block is independent |
104 | # of the context that called txn_do() |
105 | eval { |
106 | |
107 | # Need to differentiate between scalar/list context to allow for |
108 | # returning a list in scalar context to get the size of the list |
109 | if ($wantarray) { |
110 | # list context |
111 | @return_values = $coderef->(@args); |
112 | } elsif (defined $wantarray) { |
113 | # scalar context |
114 | $return_value = $coderef->(@args); |
115 | } else { |
116 | # void context |
117 | $coderef->(@args); |
118 | } |
119 | $self->txn_commit; |
120 | }; |
121 | |
122 | if ($@) { |
123 | my $error = $@; |
124 | |
125 | eval { |
126 | $self->txn_rollback; |
127 | }; |
128 | |
129 | if ($@) { |
130 | my $rollback_error = $@; |
131 | my $exception_class = "DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION"; |
132 | $self->throw_exception($error) # propagate nested rollback |
133 | if $rollback_error =~ /$exception_class/; |
134 | |
135 | $self->throw_exception( |
136 | "Transaction aborted: $error. Rollback failed: ${rollback_error}" |
137 | ); |
138 | } else { |
139 | $self->throw_exception($error); # txn failed but rollback succeeded |
140 | } |
141 | } |
142 | |
143 | return $wantarray ? @return_values : $return_value; |
a62cf8d4 |
144 | } |
145 | |
146 | 1; |