Another overhaul (hopefully one of the last ones) of the rollback handling
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage.pm
CommitLineData
4012acd8 1package DBIx::Class::Storage;
a62cf8d4 2
3use strict;
4use warnings;
5
046ad905 6use base qw/DBIx::Class/;
2ad62d97 7use mro 'c3';
046ad905 8
90d7422f 9{
10 package # Hide from PAUSE
11 DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION;
12 use base 'DBIx::Class::Exception';
13}
14
15use DBIx::Class::Carp;
9345b14c 16use DBIx::Class::Storage::BlockRunner;
90d7422f 17use Scalar::Util qw/blessed weaken/;
1bc193ac 18use DBIx::Class::Storage::TxnScopeGuard;
f43ea814 19use Try::Tiny;
fd323bf1 20use namespace::clean;
046ad905 21
90d7422f 22__PACKAGE__->mk_group_accessors(simple => qw/debug schema transaction_depth auto_savepoint savepoints/);
23__PACKAGE__->mk_group_accessors(component_class => 'cursor_class');
e4eb8ee1 24
25__PACKAGE__->cursor_class('DBIx::Class::Cursor');
26
27sub cursor { shift->cursor_class(@_); }
046ad905 28
046ad905 29=head1 NAME
30
31DBIx::Class::Storage - Generic Storage Handler
32
33=head1 DESCRIPTION
34
35A base implementation of common Storage methods. For specific
36information about L<DBI>-based storage, see L<DBIx::Class::Storage::DBI>.
37
38=head1 METHODS
39
40=head2 new
41
42Arguments: $schema
43
44Instantiates the Storage object.
45
46=cut
47
48sub new {
49 my ($self, $schema) = @_;
50
51 $self = ref $self if ref $self;
52
90d7422f 53 my $new = bless( {
90d7422f 54 savepoints => [],
55 }, $self);
046ad905 56
57 $new->set_schema($schema);
4d753fb8 58 $new->debug(1)
59 if $ENV{DBIX_CLASS_STORAGE_DBI_DEBUG} || $ENV{DBIC_TRACE};
046ad905 60
61 $new;
62}
63
64=head2 set_schema
65
66Used to reset the schema class or object which owns this
67storage object, such as during L<DBIx::Class::Schema/clone>.
68
69=cut
70
71sub set_schema {
72 my ($self, $schema) = @_;
73 $self->schema($schema);
6298a324 74 weaken $self->{schema} if ref $self->{schema};
046ad905 75}
76
77=head2 connected
78
79Returns true if we have an open storage connection, false
80if it is not (yet) open.
81
82=cut
83
a62cf8d4 84sub connected { die "Virtual method!" }
046ad905 85
86=head2 disconnect
87
88Closes any open storage connection unconditionally.
89
90=cut
91
92sub disconnect { die "Virtual method!" }
93
94=head2 ensure_connected
95
96Initiate a connection to the storage if one isn't already open.
97
98=cut
99
a62cf8d4 100sub ensure_connected { die "Virtual method!" }
046ad905 101
102=head2 throw_exception
103
104Throws an exception - croaks.
105
106=cut
107
108sub throw_exception {
109 my $self = shift;
110
2a2a7b23 111 if (ref $self and $self->schema) {
1a58752c 112 $self->schema->throw_exception(@_);
113 }
114 else {
115 DBIx::Class::Exception->throw(@_);
116 }
046ad905 117}
a62cf8d4 118
4012acd8 119=head2 txn_do
a62cf8d4 120
4012acd8 121=over 4
a62cf8d4 122
4012acd8 123=item Arguments: C<$coderef>, @coderef_args?
a62cf8d4 124
4012acd8 125=item Return Value: The return value of $coderef
126
127=back
128
129Executes C<$coderef> with (optional) arguments C<@coderef_args> atomically,
130returning its result (if any). If an exception is caught, a rollback is issued
131and the exception is rethrown. If the rollback fails, (i.e. throws an
132exception) an exception is thrown that includes a "Rollback failed" message.
133
134For example,
135
136 my $author_rs = $schema->resultset('Author')->find(1);
137 my @titles = qw/Night Day It/;
138
139 my $coderef = sub {
140 # If any one of these fails, the entire transaction fails
141 $author_rs->create_related('books', {
142 title => $_
143 }) foreach (@titles);
144
145 return $author->books;
146 };
147
148 my $rs;
20674fcd 149 try {
4012acd8 150 $rs = $schema->txn_do($coderef);
20674fcd 151 } catch {
6b89ee0b 152 my $error = shift;
20674fcd 153 # Transaction failed
90d7422f 154 die "something terrible has happened!"
6b89ee0b 155 if ($error =~ /Rollback failed/); # Rollback failed
4012acd8 156
157 deal_with_failed_transaction();
20674fcd 158 };
4012acd8 159
160In a nested transaction (calling txn_do() from within a txn_do() coderef) only
161the outermost transaction will issue a L</txn_commit>, and txn_do() can be
162called in void, scalar and list context and it will behave as expected.
163
05075aee 164Please note that all of the code in your coderef, including non-DBIx::Class
165code, is part of a transaction. This transaction may fail out halfway, or
166it may get partially double-executed (in the case that our DB connection
167failed halfway through the transaction, in which case we reconnect and
168restart the txn). Therefore it is best that any side-effects in your coderef
169are idempotent (that is, can be re-executed multiple times and get the
170same result), and that you check up on your side-effects in the case of
171transaction failure.
6500d50f 172
4012acd8 173=cut
174
175sub txn_do {
38ed54cd 176 my $self = shift;
4012acd8 177
9345b14c 178 DBIx::Class::Storage::BlockRunner->new(
179 storage => $self,
9345b14c 180 wrap_txn => 1,
7d534e68 181 retry_handler => sub {
182 $_[0]->failed_attempt_count == 1
183 and
184 ! $_[0]->storage->connected
185 },
186 )->run(@_);
a62cf8d4 187}
188
046ad905 189=head2 txn_begin
190
191Starts a transaction.
192
193See the preferred L</txn_do> method, which allows for
194an entire code block to be executed transactionally.
195
196=cut
197
90d7422f 198sub txn_begin {
199 my $self = shift;
200
201 if($self->transaction_depth == 0) {
202 $self->debugobj->txn_begin()
203 if $self->debug;
204 $self->_exec_txn_begin;
205 }
206 elsif ($self->auto_savepoint) {
207 $self->svp_begin;
208 }
209 $self->{transaction_depth}++;
210
211}
046ad905 212
213=head2 txn_commit
214
215Issues a commit of the current transaction.
216
be01f1be 217It does I<not> perform an actual storage commit unless there's a DBIx::Class
218transaction currently in effect (i.e. you called L</txn_begin>).
219
046ad905 220=cut
221
90d7422f 222sub txn_commit {
223 my $self = shift;
224
225 if ($self->transaction_depth == 1) {
226 $self->debugobj->txn_commit() if $self->debug;
227 $self->_exec_txn_commit;
228 $self->{transaction_depth}--;
398215b1 229 $self->savepoints([]);
90d7422f 230 }
231 elsif($self->transaction_depth > 1) {
232 $self->{transaction_depth}--;
233 $self->svp_release if $self->auto_savepoint;
234 }
235 else {
236 $self->throw_exception( 'Refusing to commit without a started transaction' );
237 }
238}
046ad905 239
240=head2 txn_rollback
241
242Issues a rollback of the current transaction. A nested rollback will
243throw a L<DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION> exception,
244which allows the rollback to propagate to the outermost transaction.
245
246=cut
247
90d7422f 248sub txn_rollback {
249 my $self = shift;
250
251 if ($self->transaction_depth == 1) {
252 $self->debugobj->txn_rollback() if $self->debug;
253 $self->_exec_txn_rollback;
254 $self->{transaction_depth}--;
398215b1 255 $self->savepoints([]);
90d7422f 256 }
257 elsif ($self->transaction_depth > 1) {
258 $self->{transaction_depth}--;
259
260 if ($self->auto_savepoint) {
261 $self->svp_rollback;
262 $self->svp_release;
263 }
264 else {
265 DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION->throw(
266 "A txn_rollback in nested transaction is ineffective! (depth $self->{transaction_depth})"
267 );
268 }
269 }
270 else {
271 $self->throw_exception( 'Refusing to roll back without a started transaction' );
272 }
273}
046ad905 274
84efb6d7 275# to be called by several internal stacked transaction handler codepaths
276# not for external consumption
277# *DOES NOT* throw exceptions, instead:
278# - returns false on success
279# - returns the exception on failed rollback
280sub __delicate_rollback {
281 my $self = shift;
282
283 if (
284 $self->transaction_depth > 1
285 and
286 # FIXME - the autosvp check here shouldn't be happening, it should be a role-ish thing
287 # The entire concept needs to be rethought with the storage layer... or something
288 ! $self->auto_savepoint
289 and
290 # the handle seems healthy, and there is nothing for us to do with it
291 # just go ahead and bow out, without triggering the txn_rollback() "nested exception"
292 # the unwind will eventually fail somewhere higher up if at all
293 # FIXME: a ::Storage::DBI-specific method, not a generic ::Storage one
294 $self->_seems_connected
295 ) {
296 # all above checks out - there is nothing to do on the $dbh itself
297 # just a plain soft-decrease of depth
298 $self->{transaction_depth}--;
299 return;
300 }
301
302 my $rbe;
303
304 local $@; # taking no chances
305 unless( eval { $self->txn_rollback; 1 } ) {
306
307 $rbe = $@;
308
309 # we were passed an existing exception to augment (think DESTROY stacks etc)
310 if (@_) {
311 my $exception = shift;
312
313 # append our text - THIS IS A TEMPORARY FIXUP!
314 #
315 # If the passed in exception is a reference, or an object we don't know
316 # how to augment - flattening it is just damn rude
317 if (
318 # FIXME - a better way, not liable to destroy an existing exception needs
319 # to be created. For the time being perpetuating the sin below in order
320 # to break the deadlock of which yak is being shaved first
321 0
322 and
323 length ref $$exception
324 and
325 (
326 ! defined blessed $$exception
327 or
328 ! $$exception->isa( 'DBIx::Class::Exception' )
329 )
330 ) {
331
332 ##################
333 ### FIXME - TODO
334 ##################
335
336 }
337 else {
338
339 # SUCH HIDEOUS, MUCH AUGH! (and double WOW on the s/// at the end below)
340 $rbe =~ s/ at .+? line \d+$//;
341
342 (
343 (
344 defined blessed $$exception
345 and
346 $$exception->isa( 'DBIx::Class::Exception' )
347 )
348 ? (
349 $$exception->{msg} =
350 "Transaction aborted: $$exception->{msg}. Rollback failed: $rbe"
351 )
352 : (
353 $$exception =
354 "Transaction aborted: $$exception. Rollback failed: $rbe"
355 )
356 ) =~ s/Transaction aborted: (?=Transaction aborted:)//;
357 }
358 }
359 }
360
361 return $rbe;
362}
363
adb3554a 364=head2 svp_begin
365
360dc8a5 366Arguments: $savepoint_name?
adb3554a 367
360dc8a5 368Created a new savepoint using the name provided as argument. If no name
369is provided, a random name will be used.
adb3554a 370
371=cut
372
90d7422f 373sub svp_begin {
374 my ($self, $name) = @_;
375
376 $self->throw_exception ("You can't use savepoints outside a transaction")
377 unless $self->transaction_depth;
378
379 my $exec = $self->can('_exec_svp_begin')
380 or $self->throw_exception ("Your Storage implementation doesn't support savepoints");
381
382 $name = $self->_svp_generate_name
383 unless defined $name;
384
385 push @{ $self->{savepoints} }, $name;
386
387 $self->debugobj->svp_begin($name) if $self->debug;
388
389 $exec->($self, $name);
390}
391
392sub _svp_generate_name {
393 my ($self) = @_;
394 return 'savepoint_'.scalar(@{ $self->{'savepoints'} });
395}
396
adb3554a 397
398=head2 svp_release
399
360dc8a5 400Arguments: $savepoint_name?
adb3554a 401
360dc8a5 402Release the savepoint provided as argument. If none is provided,
403release the savepoint created most recently. This will implicitly
404release all savepoints created after the one explicitly released as well.
adb3554a 405
406=cut
407
90d7422f 408sub svp_release {
409 my ($self, $name) = @_;
410
411 $self->throw_exception ("You can't use savepoints outside a transaction")
412 unless $self->transaction_depth;
413
414 my $exec = $self->can('_exec_svp_release')
415 or $self->throw_exception ("Your Storage implementation doesn't support savepoints");
416
417 if (defined $name) {
418 my @stack = @{ $self->savepoints };
419 my $svp;
420
421 do { $svp = pop @stack } until $svp eq $name;
422
423 $self->throw_exception ("Savepoint '$name' does not exist")
424 unless $svp;
425
426 $self->savepoints(\@stack); # put back what's left
427 }
428 else {
429 $name = pop @{ $self->savepoints }
430 or $self->throw_exception('No savepoints to release');;
431 }
432
433 $self->debugobj->svp_release($name) if $self->debug;
434
435 $exec->($self, $name);
436}
adb3554a 437
438=head2 svp_rollback
439
360dc8a5 440Arguments: $savepoint_name?
adb3554a 441
360dc8a5 442Rollback to the savepoint provided as argument. If none is provided,
443rollback to the savepoint created most recently. This will implicitly
444release all savepoints created after the savepoint we rollback to.
adb3554a 445
446=cut
447
90d7422f 448sub svp_rollback {
449 my ($self, $name) = @_;
450
451 $self->throw_exception ("You can't use savepoints outside a transaction")
452 unless $self->transaction_depth;
453
454 my $exec = $self->can('_exec_svp_rollback')
455 or $self->throw_exception ("Your Storage implementation doesn't support savepoints");
456
457 if (defined $name) {
458 my @stack = @{ $self->savepoints };
459 my $svp;
460
461 # a rollback doesn't remove the named savepoint,
462 # only everything after it
463 while (@stack and $stack[-1] ne $name) {
464 pop @stack
465 };
466
467 $self->throw_exception ("Savepoint '$name' does not exist")
468 unless @stack;
469
470 $self->savepoints(\@stack); # put back what's left
471 }
472 else {
473 $name = $self->savepoints->[-1]
474 or $self->throw_exception('No savepoints to rollback');;
475 }
476
477 $self->debugobj->svp_rollback($name) if $self->debug;
478
479 $exec->($self, $name);
480}
adb3554a 481
11544e1d 482=head2 txn_scope_guard
3b7f3eac 483
11544e1d 484An alternative way of transaction handling based on
485L<DBIx::Class::Storage::TxnScopeGuard>:
1bc193ac 486
11544e1d 487 my $txn_guard = $storage->txn_scope_guard;
89028f42 488
47d7b769 489 $result->col1("val1");
490 $result->update;
89028f42 491
11544e1d 492 $txn_guard->commit;
89028f42 493
11544e1d 494If an exception occurs, or the guard object otherwise leaves the scope
495before C<< $txn_guard->commit >> is called, the transaction will be rolled
496back by an explicit L</txn_rollback> call. In essence this is akin to
497using a L</txn_begin>/L</txn_commit> pair, without having to worry
498about calling L</txn_rollback> at the right places. Note that since there
499is no defined code closure, there will be no retries and other magic upon
500database disconnection. If you need such functionality see L</txn_do>.
1bc193ac 501
502=cut
503
504sub txn_scope_guard {
505 return DBIx::Class::Storage::TxnScopeGuard->new($_[0]);
506}
507
046ad905 508=head2 sql_maker
509
510Returns a C<sql_maker> object - normally an object of class
d5dedbd6 511C<DBIx::Class::SQLMaker>.
046ad905 512
513=cut
514
515sub sql_maker { die "Virtual method!" }
516
517=head2 debug
518
f92a9d79 519Causes trace information to be emitted on the L</debugobj> object.
520(or C<STDERR> if L</debugobj> has not specifically been set).
046ad905 521
522This is the equivalent to setting L</DBIC_TRACE> in your
523shell environment.
524
525=head2 debugfh
526
4d93345c 527An opportunistic proxy to L<< ->debugobj->debugfh(@_)
528|DBIx::Class::Storage::Statistics/debugfh >>
529If the currently set L</debugobj> does not have a L</debugfh> method, caling
530this is a no-op.
046ad905 531
532=cut
533
534sub debugfh {
535 my $self = shift;
536
537 if ($self->debugobj->can('debugfh')) {
538 return $self->debugobj->debugfh(@_);
539 }
540}
541
542=head2 debugobj
543
544Sets or retrieves the object used for metric collection. Defaults to an instance
545of L<DBIx::Class::Storage::Statistics> that is compatible with the original
546method of using a coderef as a callback. See the aforementioned Statistics
547class for more information.
548
4d753fb8 549=cut
550
551sub debugobj {
552 my $self = shift;
553
554 if (@_) {
555 return $self->{debugobj} = $_[0];
556 }
557
558 $self->{debugobj} ||= do {
559 if (my $profile = $ENV{DBIC_TRACE_PROFILE}) {
560 require DBIx::Class::Storage::Debug::PrettyPrint;
6e102c8f 561 my @pp_args;
562
4d753fb8 563 if ($profile =~ /^\.?\//) {
564 require Config::Any;
565
566 my $cfg = try {
567 Config::Any->load_files({ files => [$profile], use_ext => 1 });
568 } catch {
569 # sanitize the error message a bit
570 $_ =~ s/at \s+ .+ Storage\.pm \s line \s \d+ $//x;
571 $self->throw_exception("Failure processing \$ENV{DBIC_TRACE_PROFILE}: $_");
572 };
573
6e102c8f 574 @pp_args = values %{$cfg->[0]};
4d753fb8 575 }
576 else {
6e102c8f 577 @pp_args = { profile => $profile };
578 }
579
580 # FIXME - FRAGILE
581 # Hash::Merge is a sorry piece of shit and tramples all over $@
582 # *without* throwing an exception
583 # This is a rather serious problem in the debug codepath
584 # Insulate the condition here with a try{} until a review of
585 # DBIx::Class::Storage::Debug::PrettyPrint takes place
586 # we do rethrow the error unconditionally, the only reason
587 # to try{} is to preserve the precise state of $@ (down
588 # to the scalar (if there is one) address level)
589 #
590 # Yes I am aware this is fragile and TxnScopeGuard needs
591 # a better fix. This is another yak to shave... :(
592 try {
593 DBIx::Class::Storage::Debug::PrettyPrint->new(@pp_args);
594 } catch {
595 $self->throw_exception($_);
4d753fb8 596 }
597 }
598 else {
599 require DBIx::Class::Storage::Statistics;
600 DBIx::Class::Storage::Statistics->new
601 }
602 };
603}
604
046ad905 605=head2 debugcb
606
607Sets a callback to be executed each time a statement is run; takes a sub
608reference. Callback is executed as $sub->($op, $info) where $op is
609SELECT/INSERT/UPDATE/DELETE and $info is what would normally be printed.
610
f92a9d79 611See L</debugobj> for a better way.
046ad905 612
613=cut
614
615sub debugcb {
616 my $self = shift;
617
618 if ($self->debugobj->can('callback')) {
619 return $self->debugobj->callback(@_);
620 }
621}
622
e4eb8ee1 623=head2 cursor_class
046ad905 624
625The cursor class for this Storage object.
626
627=cut
628
046ad905 629=head2 deploy
630
631Deploy the tables to storage (CREATE TABLE and friends in a SQL-based
632Storage class). This would normally be called through
633L<DBIx::Class::Schema/deploy>.
634
635=cut
636
637sub deploy { die "Virtual method!" }
638
a3eaff0e 639=head2 connect_info
640
641The arguments of C<connect_info> are always a single array reference,
642and are Storage-handler specific.
643
644This is normally accessed via L<DBIx::Class::Schema/connection>, which
645encapsulates its argument list in an arrayref before calling
646C<connect_info> here.
647
648=cut
649
046ad905 650sub connect_info { die "Virtual method!" }
a3eaff0e 651
652=head2 select
653
654Handle a select statement.
655
656=cut
657
658sub select { die "Virtual method!" }
659
660=head2 insert
661
662Handle an insert statement.
663
664=cut
665
046ad905 666sub insert { die "Virtual method!" }
a3eaff0e 667
668=head2 update
669
670Handle an update statement.
671
672=cut
673
046ad905 674sub update { die "Virtual method!" }
a3eaff0e 675
676=head2 delete
677
678Handle a delete statement.
679
680=cut
681
046ad905 682sub delete { die "Virtual method!" }
a3eaff0e 683
684=head2 select_single
685
686Performs a select, fetch and return of data - handles a single row
687only.
688
689=cut
690
046ad905 691sub select_single { die "Virtual method!" }
a3eaff0e 692
693=head2 columns_info_for
694
c22c7625 695Returns metadata for the given source's columns. This
696is *deprecated*, and will be removed before 1.0. You should
697be specifying the metadata yourself if you need it.
a3eaff0e 698
699=cut
700
046ad905 701sub columns_info_for { die "Virtual method!" }
702
703=head1 ENVIRONMENT VARIABLES
704
705=head2 DBIC_TRACE
706
707If C<DBIC_TRACE> is set then trace information
f92a9d79 708is produced (as when the L</debug> method is set).
046ad905 709
710If the value is of the form C<1=/path/name> then the trace output is
711written to the file C</path/name>.
712
713This environment variable is checked when the storage object is first
fd323bf1 714created (when you call connect on your schema). So, run-time changes
715to this environment variable will not take effect unless you also
046ad905 716re-connect on your schema.
717
b6cd6478 718=head2 DBIC_TRACE_PROFILE
719
2514a73f 720If C<DBIC_TRACE_PROFILE> is set, L<DBIx::Class::Storage::Debug::PrettyPrint>
b6cd6478 721will be used to format the output from C<DBIC_TRACE>. The value it
722is set to is the C<profile> that it will be used. If the value is a
723filename the file is read with L<Config::Any> and the results are
724used as the configuration for tracing. See L<SQL::Abstract::Tree/new>
725for what that structure should look like.
726
046ad905 727=head2 DBIX_CLASS_STORAGE_DBI_DEBUG
728
729Old name for DBIC_TRACE
730
ace385bd 731=head1 SEE ALSO
732
2f0790c4 733L<DBIx::Class::Storage::DBI> - reference storage implementation using
734SQL::Abstract and DBI.
ace385bd 735
a2bd3796 736=head1 FURTHER QUESTIONS?
046ad905 737
a2bd3796 738Check the list of L<additional DBIC resources|DBIx::Class/GETTING HELP/SUPPORT>.
046ad905 739
a2bd3796 740=head1 COPYRIGHT AND LICENSE
046ad905 741
a2bd3796 742This module is free software L<copyright|DBIx::Class/COPYRIGHT AND LICENSE>
743by the L<DBIx::Class (DBIC) authors|DBIx::Class/AUTHORS>. You can
744redistribute it and/or modify it under the same terms as the
745L<DBIx::Class library|DBIx::Class/COPYRIGHT AND LICENSE>.
046ad905 746
747=cut
748
a62cf8d4 7491;