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