reshuffling the division of labor between Storage and Storage::DBI (not complete)
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage.pm
1 package DBIx::Class::Storage;
2
3 use strict;
4 use warnings;
5
6 use base qw/DBIx::Class/;
7
8 use Scalar::Util qw/weaken/;
9 use Carp::Clan qw/DBIx::Class/;
10
11 __PACKAGE__->load_components(qw/AccessorGroup/);
12 __PACKAGE__->mk_group_accessors('simple' => qw/debug debugobj schema/);
13
14 package # Hide from PAUSE
15     DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION;
16
17 use overload '"' => sub {
18   'DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION'
19 };
20
21 sub new {
22   my $class = shift;
23   my $self = {};
24   return bless $self, $class;
25 }
26
27 package DBIx::Class::Storage;
28
29 =head1 NAME
30
31 DBIx::Class::Storage - Generic Storage Handler
32
33 =head1 DESCRIPTION
34
35 A base implementation of common Storage methods.  For specific
36 information about L<DBI>-based storage, see L<DBIx::Class::Storage::DBI>.
37
38 =head1 METHODS
39
40 =head2 new
41
42 Arguments: $schema
43
44 Instantiates the Storage object.
45
46 =cut
47
48 sub new {
49   my ($self, $schema) = @_;
50
51   $self = ref $self if ref $self;
52
53   my $new = {};
54   bless $new, $self;
55
56   $new->set_schema($schema);
57   $new->debugobj(new DBIx::Class::Storage::Statistics());
58
59   my $fh;
60
61   my $debug_env = $ENV{DBIX_CLASS_STORAGE_DBI_DEBUG}
62                   || $ENV{DBIC_TRACE};
63
64   if (defined($debug_env) && ($debug_env =~ /=(.+)$/)) {
65     $fh = IO::File->new($1, 'w')
66       or $new->throw_exception("Cannot open trace file $1");
67   } else {
68     $fh = IO::File->new('>&STDERR');
69   }
70
71   $new->debugfh($fh);
72   $new->debug(1) if $debug_env;
73
74   $new;
75 }
76
77 =head2 set_schema
78
79 Used to reset the schema class or object which owns this
80 storage object, such as during L<DBIx::Class::Schema/clone>.
81
82 =cut
83
84 sub set_schema {
85   my ($self, $schema) = @_;
86   $self->schema($schema);
87   weaken($self->{schema}) if ref $self->{schema};
88 }
89
90 =head2 connected
91
92 Returns true if we have an open storage connection, false
93 if it is not (yet) open.
94
95 =cut
96
97 sub connected { die "Virtual method!" }
98
99 =head2 disconnect
100
101 Closes any open storage connection unconditionally.
102
103 =cut
104
105 sub disconnect { die "Virtual method!" }
106
107 =head2 ensure_connected
108
109 Initiate a connection to the storage if one isn't already open.
110
111 =cut
112
113 sub ensure_connected { die "Virtual method!" }
114
115 =head2 throw_exception
116
117 Throws an exception - croaks.
118
119 =cut
120
121 sub throw_exception {
122   my $self = shift;
123
124   $self->schema->throw_exception(@_) if $self->schema;
125   croak @_;
126 }
127
128 =head2 txn_do
129
130 =over 4
131
132 =item Arguments: C<$coderef>, @coderef_args?
133
134 =item Return Value: The return value of $coderef
135
136 =back
137
138 Executes C<$coderef> with (optional) arguments C<@coderef_args> atomically,
139 returning its result (if any). If an exception is caught, a rollback is issued
140 and the exception is rethrown. If the rollback fails, (i.e. throws an
141 exception) an exception is thrown that includes a "Rollback failed" message.
142
143 For example,
144
145   my $author_rs = $schema->resultset('Author')->find(1);
146   my @titles = qw/Night Day It/;
147
148   my $coderef = sub {
149     # If any one of these fails, the entire transaction fails
150     $author_rs->create_related('books', {
151       title => $_
152     }) foreach (@titles);
153
154     return $author->books;
155   };
156
157   my $rs;
158   eval {
159     $rs = $schema->txn_do($coderef);
160   };
161
162   if ($@) {                                  # Transaction failed
163     die "something terrible has happened!"   #
164       if ($@ =~ /Rollback failed/);          # Rollback failed
165
166     deal_with_failed_transaction();
167   }
168
169 In a nested transaction (calling txn_do() from within a txn_do() coderef) only
170 the outermost transaction will issue a L</txn_commit>, and txn_do() can be
171 called in void, scalar and list context and it will behave as expected.
172
173 =cut
174
175 sub txn_do {
176   my ($self, $coderef, @args) = @_;
177
178   ref $coderef eq 'CODE' or $self->throw_exception
179     ('$coderef must be a CODE reference');
180
181   my (@return_values, $return_value);
182
183   $self->txn_begin; # If this throws an exception, no rollback is needed
184
185   my $wantarray = wantarray; # Need to save this since the context
186                              # inside the eval{} block is independent
187                              # of the context that called txn_do()
188   eval {
189
190     # Need to differentiate between scalar/list context to allow for
191     # returning a list in scalar context to get the size of the list
192     if ($wantarray) {
193       # list context
194       @return_values = $coderef->(@args);
195     } elsif (defined $wantarray) {
196       # scalar context
197       $return_value = $coderef->(@args);
198     } else {
199       # void context
200       $coderef->(@args);
201     }
202     $self->txn_commit;
203   };
204
205   if ($@) {
206     my $error = $@;
207
208     eval {
209       $self->txn_rollback;
210     };
211
212     if ($@) {
213       my $rollback_error = $@;
214       my $exception_class = "DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION";
215       $self->throw_exception($error)  # propagate nested rollback
216         if $rollback_error =~ /$exception_class/;
217
218       $self->throw_exception(
219         "Transaction aborted: $error. Rollback failed: ${rollback_error}"
220       );
221     } else {
222       $self->throw_exception($error); # txn failed but rollback succeeded
223     }
224   }
225
226   return $wantarray ? @return_values : $return_value;
227 }
228
229 =head2 txn_begin
230
231 Starts a transaction.
232
233 See the preferred L</txn_do> method, which allows for
234 an entire code block to be executed transactionally.
235
236 =cut
237
238 sub txn_begin { die "Virtual method!" }
239
240 =head2 txn_commit
241
242 Issues a commit of the current transaction.
243
244 =cut
245
246 sub txn_commit { die "Virtual method!" }
247
248 =head2 txn_rollback
249
250 Issues a rollback of the current transaction. A nested rollback will
251 throw a L<DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION> exception,
252 which allows the rollback to propagate to the outermost transaction.
253
254 =cut
255
256 sub txn_rollback { die "Virtual method!" }
257
258 =head2 sql_maker
259
260 Returns a C<sql_maker> object - normally an object of class
261 C<DBIC::SQL::Abstract>.
262
263 =cut
264
265 sub sql_maker { die "Virtual method!" }
266
267 =head2 debug
268
269 Causes trace information to be emitted on the C<debugobj> object.
270 (or C<STDERR> if C<debugobj> has not specifically been set).
271
272 This is the equivalent to setting L</DBIC_TRACE> in your
273 shell environment.
274
275 =head2 debugfh
276
277 Set or retrieve the filehandle used for trace/debug output.  This should be
278 an IO::Handle compatible ojbect (only the C<print> method is used.  Initially
279 set to be STDERR - although see information on the
280 L<DBIC_TRACE> environment variable.
281
282 =cut
283
284 sub debugfh {
285     my $self = shift;
286
287     if ($self->debugobj->can('debugfh')) {
288         return $self->debugobj->debugfh(@_);
289     }
290 }
291
292 =head2 debugobj
293
294 Sets or retrieves the object used for metric collection. Defaults to an instance
295 of L<DBIx::Class::Storage::Statistics> that is compatible with the original
296 method of using a coderef as a callback.  See the aforementioned Statistics
297 class for more information.
298
299 =head2 debugcb
300
301 Sets a callback to be executed each time a statement is run; takes a sub
302 reference.  Callback is executed as $sub->($op, $info) where $op is
303 SELECT/INSERT/UPDATE/DELETE and $info is what would normally be printed.
304
305 See L<debugobj> for a better way.
306
307 =cut
308
309 sub debugcb {
310     my $self = shift;
311
312     if ($self->debugobj->can('callback')) {
313         return $self->debugobj->callback(@_);
314     }
315 }
316
317 =head2 cursor
318
319 The cursor class for this Storage object.
320
321 =cut
322
323 sub cursor { die "Virtual method!" }
324
325 =head2 deploy
326
327 Deploy the tables to storage (CREATE TABLE and friends in a SQL-based
328 Storage class). This would normally be called through
329 L<DBIx::Class::Schema/deploy>.
330
331 =cut
332
333 sub deploy { die "Virtual method!" }
334
335 sub on_connect_do { die "Virtual method!" }
336 sub connect_info { die "Virtual method!" }
337 sub insert { die "Virtual method!" }
338 sub update { die "Virtual method!" }
339 sub delete { die "Virtual method!" }
340 sub select { die "Virtual method!" }
341 sub select_single { die "Virtual method!" }
342 sub columns_info_for { die "Virtual method!" }
343
344 =head1 ENVIRONMENT VARIABLES
345
346 =head2 DBIC_TRACE
347
348 If C<DBIC_TRACE> is set then trace information
349 is produced (as when the L<debug> method is set).
350
351 If the value is of the form C<1=/path/name> then the trace output is
352 written to the file C</path/name>.
353
354 This environment variable is checked when the storage object is first
355 created (when you call connect on your schema).  So, run-time changes 
356 to this environment variable will not take effect unless you also 
357 re-connect on your schema.
358
359 =head2 DBIX_CLASS_STORAGE_DBI_DEBUG
360
361 Old name for DBIC_TRACE
362
363 =head1 AUTHORS
364
365 Matt S. Trout <mst@shadowcatsystems.co.uk>
366
367 Andy Grundman <andy@hybridized.org>
368
369 =head1 LICENSE
370
371 You may distribute this code under the same terms as Perl itself.
372
373 =cut
374
375 1;