move txn_do to Storage
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage.pm
CommitLineData
4012acd8 1package DBIx::Class::Storage;
a62cf8d4 2
3use strict;
4use warnings;
5
4012acd8 6package # Hide from PAUSE
7 DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION;
8
9use overload '"' => sub {
10 'DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION'
11};
12
13sub new {
14 my $class = shift;
15 my $self = {};
16 return bless $self, $class;
17}
18
19package DBIx::Class::Storage;
20
a62cf8d4 21sub new { die "Virtual method!" }
82cc0386 22sub set_schema { die "Virtual method!" }
a62cf8d4 23sub debug { die "Virtual method!" }
24sub debugcb { die "Virtual method!" }
25sub debugfh { die "Virtual method!" }
96761eee 26sub debugobj { die "Virtual method!" }
27sub cursor { die "Virtual method!" }
a62cf8d4 28sub disconnect { die "Virtual method!" }
29sub connected { die "Virtual method!" }
30sub ensure_connected { die "Virtual method!" }
96761eee 31sub on_connect_do { die "Virtual method!" }
32sub connect_info { die "Virtual method!" }
a62cf8d4 33sub sql_maker { die "Virtual method!" }
34sub txn_begin { die "Virtual method!" }
35sub txn_commit { die "Virtual method!" }
36sub txn_rollback { die "Virtual method!" }
37sub insert { die "Virtual method!" }
38sub update { die "Virtual method!" }
39sub delete { die "Virtual method!" }
40sub select { die "Virtual method!" }
41sub select_single { die "Virtual method!" }
42sub columns_info_for { die "Virtual method!" }
4012acd8 43sub 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
55Executes C<$coderef> with (optional) arguments C<@coderef_args> atomically,
56returning its result (if any). If an exception is caught, a rollback is issued
57and the exception is rethrown. If the rollback fails, (i.e. throws an
58exception) an exception is thrown that includes a "Rollback failed" message.
59
60For 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
86In a nested transaction (calling txn_do() from within a txn_do() coderef) only
87the outermost transaction will issue a L</txn_commit>, and txn_do() can be
88called in void, scalar and list context and it will behave as expected.
89
90=cut
91
92sub 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
1461;