File Coverage

blib/lib/DBIx/Class/Storage.pm
Criterion Covered Total %
statement 150 186 80.6
branch 64 84 76.1
condition 19 23 82.6
subroutine 33 51 64.7
pod 26 27 96.3
total 292 371 78.7


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