1 package DBIx::Class::Storage;
6 use base qw/DBIx::Class/;
10 package # Hide from PAUSE
11 DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION;
12 use base 'DBIx::Class::Exception';
15 use DBIx::Class::Carp;
16 use DBIx::Class::Storage::BlockRunner;
17 use Scalar::Util qw/blessed weaken/;
18 use DBIx::Class::Storage::TxnScopeGuard;
22 __PACKAGE__->mk_group_accessors(simple => qw/debug schema transaction_depth auto_savepoint savepoints/);
23 __PACKAGE__->mk_group_accessors(component_class => 'cursor_class');
25 __PACKAGE__->cursor_class('DBIx::Class::Cursor');
27 sub cursor { shift->cursor_class(@_); }
31 DBIx::Class::Storage - Generic Storage Handler
35 A base implementation of common Storage methods. For specific
36 information about L<DBI>-based storage, see L<DBIx::Class::Storage::DBI>.
44 Instantiates the Storage object.
49 my ($self, $schema) = @_;
51 $self = ref $self if ref $self;
57 $new->set_schema($schema);
59 if $ENV{DBIX_CLASS_STORAGE_DBI_DEBUG} || $ENV{DBIC_TRACE};
66 Used to reset the schema class or object which owns this
67 storage object, such as during L<DBIx::Class::Schema/clone>.
72 my ($self, $schema) = @_;
73 $self->schema($schema);
74 weaken $self->{schema} if ref $self->{schema};
79 Returns true if we have an open storage connection, false
80 if it is not (yet) open.
84 sub connected { die "Virtual method!" }
88 Closes any open storage connection unconditionally.
92 sub disconnect { die "Virtual method!" }
94 =head2 ensure_connected
96 Initiate a connection to the storage if one isn't already open.
100 sub ensure_connected { die "Virtual method!" }
102 =head2 throw_exception
104 Throws an exception - croaks.
108 sub throw_exception {
111 if (ref $self and $self->schema) {
112 $self->schema->throw_exception(@_);
115 DBIx::Class::Exception->throw(@_);
123 =item Arguments: C<$coderef>, @coderef_args?
125 =item Return Value: The return value of $coderef
129 Executes C<$coderef> with (optional) arguments C<@coderef_args> atomically,
130 returning its result (if any). If an exception is caught, a rollback is issued
131 and the exception is rethrown. If the rollback fails, (i.e. throws an
132 exception) an exception is thrown that includes a "Rollback failed" message.
136 my $author_rs = $schema->resultset('Author')->find(1);
137 my @titles = qw/Night Day It/;
140 # If any one of these fails, the entire transaction fails
141 $author_rs->create_related('books', {
143 }) foreach (@titles);
145 return $author->books;
150 $rs = $schema->txn_do($coderef);
154 die "something terrible has happened!"
155 if ($error =~ /Rollback failed/); # Rollback failed
157 deal_with_failed_transaction();
160 In a nested transaction (calling txn_do() from within a txn_do() coderef) only
161 the outermost transaction will issue a L</txn_commit>, and txn_do() can be
162 called in void, scalar and list context and it will behave as expected.
164 Please note that all of the code in your coderef, including non-DBIx::Class
165 code, is part of a transaction. This transaction may fail out halfway, or
166 it may get partially double-executed (in the case that our DB connection
167 failed halfway through the transaction, in which case we reconnect and
168 restart the txn). Therefore it is best that any side-effects in your coderef
169 are idempotent (that is, can be re-executed multiple times and get the
170 same result), and that you check up on your side-effects in the case of
178 DBIx::Class::Storage::BlockRunner->new(
181 retry_handler => sub {
182 $_[0]->failed_attempt_count == 1
184 ! $_[0]->storage->connected
191 Starts a transaction.
193 See the preferred L</txn_do> method, which allows for
194 an entire code block to be executed transactionally.
201 if($self->transaction_depth == 0) {
202 $self->debugobj->txn_begin()
204 $self->_exec_txn_begin;
206 elsif ($self->auto_savepoint) {
209 $self->{transaction_depth}++;
215 Issues a commit of the current transaction.
217 It does I<not> perform an actual storage commit unless there's a DBIx::Class
218 transaction currently in effect (i.e. you called L</txn_begin>).
225 if ($self->transaction_depth == 1) {
226 $self->debugobj->txn_commit() if $self->debug;
227 $self->_exec_txn_commit;
228 $self->{transaction_depth}--;
229 $self->savepoints([]);
231 elsif($self->transaction_depth > 1) {
232 $self->{transaction_depth}--;
233 $self->svp_release if $self->auto_savepoint;
236 $self->throw_exception( 'Refusing to commit without a started transaction' );
242 Issues a rollback of the current transaction. A nested rollback will
243 throw a L<DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION> exception,
244 which allows the rollback to propagate to the outermost transaction.
251 if ($self->transaction_depth == 1) {
252 $self->debugobj->txn_rollback() if $self->debug;
253 $self->{transaction_depth}--;
255 # in case things get really hairy - just disconnect
256 eval { $self->_exec_txn_rollback; 1 } or do {
257 my $rollback_error = $@;
259 # whatever happens, too low down the stack to care
260 # FIXME - revisit if stackable exceptions become a thing
261 eval { $self->disconnect };
266 $self->savepoints([]);
268 elsif ($self->transaction_depth > 1) {
269 $self->{transaction_depth}--;
271 if ($self->auto_savepoint) {
276 DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION->throw(
277 "A txn_rollback in nested transaction is ineffective! (depth $self->{transaction_depth})"
282 $self->throw_exception( 'Refusing to roll back without a started transaction' );
286 # to be called by several internal stacked transaction handler codepaths
287 # not for external consumption
288 # *DOES NOT* throw exceptions, instead:
289 # - returns false on success
290 # - returns the exception on failed rollback
291 sub __delicate_rollback {
295 ( $self->transaction_depth || 0 ) > 1
297 # FIXME - the autosvp check here shouldn't be happening, it should be a role-ish thing
298 # The entire concept needs to be rethought with the storage layer... or something
299 ! $self->auto_savepoint
301 # the handle seems healthy, and there is nothing for us to do with it
302 # just go ahead and bow out, without triggering the txn_rollback() "nested exception"
303 # the unwind will eventually fail somewhere higher up if at all
304 # FIXME: a ::Storage::DBI-specific method, not a generic ::Storage one
305 $self->_seems_connected
307 # all above checks out - there is nothing to do on the $dbh itself
308 # just a plain soft-decrease of depth
309 $self->{transaction_depth}--;
315 local $@; # taking no chances
316 unless( eval { $self->txn_rollback; 1 } ) {
320 # we were passed an existing exception to augment (think DESTROY stacks etc)
322 my $exception = shift;
324 # append our text - THIS IS A TEMPORARY FIXUP!
326 # If the passed in exception is a reference, or an object we don't know
327 # how to augment - flattening it is just damn rude
329 # FIXME - a better way, not liable to destroy an existing exception needs
330 # to be created. For the time being perpetuating the sin below in order
331 # to break the deadlock of which yak is being shaved first
334 length ref $$exception
337 ! defined blessed $$exception
339 ! $$exception->isa( 'DBIx::Class::Exception' )
350 # SUCH HIDEOUS, MUCH AUGH! (and double WOW on the s/// at the end below)
351 $rbe =~ s/ at .+? line \d+$//;
355 defined blessed $$exception
357 $$exception->isa( 'DBIx::Class::Exception' )
361 "Transaction aborted: $$exception->{msg}. Rollback failed: $rbe"
365 "Transaction aborted: $$exception. Rollback failed: $rbe"
367 ) =~ s/Transaction aborted: (?=Transaction aborted:)//;
377 Arguments: $savepoint_name?
379 Created a new savepoint using the name provided as argument. If no name
380 is provided, a random name will be used.
385 my ($self, $name) = @_;
387 $self->throw_exception ("You can't use savepoints outside a transaction")
388 unless $self->transaction_depth;
390 my $exec = $self->can('_exec_svp_begin')
391 or $self->throw_exception ("Your Storage implementation doesn't support savepoints");
393 $name = $self->_svp_generate_name
394 unless defined $name;
396 push @{ $self->{savepoints} }, $name;
398 $self->debugobj->svp_begin($name) if $self->debug;
400 $exec->($self, $name);
403 sub _svp_generate_name {
405 return 'savepoint_'.scalar(@{ $self->{'savepoints'} });
411 Arguments: $savepoint_name?
413 Release the savepoint provided as argument. If none is provided,
414 release the savepoint created most recently. This will implicitly
415 release all savepoints created after the one explicitly released as well.
420 my ($self, $name) = @_;
422 $self->throw_exception ("You can't use savepoints outside a transaction")
423 unless $self->transaction_depth;
425 my $exec = $self->can('_exec_svp_release')
426 or $self->throw_exception ("Your Storage implementation doesn't support savepoints");
429 my @stack = @{ $self->savepoints };
432 do { $svp = pop @stack } until $svp eq $name;
434 $self->throw_exception ("Savepoint '$name' does not exist")
437 $self->savepoints(\@stack); # put back what's left
440 $name = pop @{ $self->savepoints }
441 or $self->throw_exception('No savepoints to release');;
444 $self->debugobj->svp_release($name) if $self->debug;
446 $exec->($self, $name);
451 Arguments: $savepoint_name?
453 Rollback to the savepoint provided as argument. If none is provided,
454 rollback to the savepoint created most recently. This will implicitly
455 release all savepoints created after the savepoint we rollback to.
460 my ($self, $name) = @_;
462 $self->throw_exception ("You can't use savepoints outside a transaction")
463 unless $self->transaction_depth;
465 my $exec = $self->can('_exec_svp_rollback')
466 or $self->throw_exception ("Your Storage implementation doesn't support savepoints");
469 my @stack = @{ $self->savepoints };
472 # a rollback doesn't remove the named savepoint,
473 # only everything after it
474 while (@stack and $stack[-1] ne $name) {
478 $self->throw_exception ("Savepoint '$name' does not exist")
481 $self->savepoints(\@stack); # put back what's left
484 $name = $self->savepoints->[-1]
485 or $self->throw_exception('No savepoints to rollback');;
488 $self->debugobj->svp_rollback($name) if $self->debug;
490 $exec->($self, $name);
493 =head2 txn_scope_guard
495 An alternative way of transaction handling based on
496 L<DBIx::Class::Storage::TxnScopeGuard>:
498 my $txn_guard = $storage->txn_scope_guard;
500 $result->col1("val1");
505 If an exception occurs, or the guard object otherwise leaves the scope
506 before C<< $txn_guard->commit >> is called, the transaction will be rolled
507 back by an explicit L</txn_rollback> call. In essence this is akin to
508 using a L</txn_begin>/L</txn_commit> pair, without having to worry
509 about calling L</txn_rollback> at the right places. Note that since there
510 is no defined code closure, there will be no retries and other magic upon
511 database disconnection. If you need such functionality see L</txn_do>.
515 sub txn_scope_guard {
516 return DBIx::Class::Storage::TxnScopeGuard->new($_[0]);
521 Returns a C<sql_maker> object - normally an object of class
522 C<DBIx::Class::SQLMaker>.
526 sub sql_maker { die "Virtual method!" }
530 Causes trace information to be emitted on the L</debugobj> object.
531 (or C<STDERR> if L</debugobj> has not specifically been set).
533 This is the equivalent to setting L</DBIC_TRACE> in your
538 An opportunistic proxy to L<< ->debugobj->debugfh(@_)
539 |DBIx::Class::Storage::Statistics/debugfh >>
540 If the currently set L</debugobj> does not have a L</debugfh> method, caling
548 if ($self->debugobj->can('debugfh')) {
549 return $self->debugobj->debugfh(@_);
555 Sets or retrieves the object used for metric collection. Defaults to an instance
556 of L<DBIx::Class::Storage::Statistics> that is compatible with the original
557 method of using a coderef as a callback. See the aforementioned Statistics
558 class for more information.
566 return $self->{debugobj} = $_[0];
569 $self->{debugobj} ||= do {
570 if (my $profile = $ENV{DBIC_TRACE_PROFILE}) {
571 require DBIx::Class::Storage::Debug::PrettyPrint;
574 if ($profile =~ /^\.?\//) {
578 Config::Any->load_files({ files => [$profile], use_ext => 1 });
580 # sanitize the error message a bit
581 $_ =~ s/at \s+ .+ Storage\.pm \s line \s \d+ $//x;
582 $self->throw_exception("Failure processing \$ENV{DBIC_TRACE_PROFILE}: $_");
585 @pp_args = values %{$cfg->[0]};
588 @pp_args = { profile => $profile };
592 # Hash::Merge is a sorry piece of shit and tramples all over $@
593 # *without* throwing an exception
594 # This is a rather serious problem in the debug codepath
595 # Insulate the condition here with a try{} until a review of
596 # DBIx::Class::Storage::Debug::PrettyPrint takes place
597 # we do rethrow the error unconditionally, the only reason
598 # to try{} is to preserve the precise state of $@ (down
599 # to the scalar (if there is one) address level)
601 # Yes I am aware this is fragile and TxnScopeGuard needs
602 # a better fix. This is another yak to shave... :(
604 DBIx::Class::Storage::Debug::PrettyPrint->new(@pp_args);
606 $self->throw_exception($_);
610 require DBIx::Class::Storage::Statistics;
611 DBIx::Class::Storage::Statistics->new
618 Sets a callback to be executed each time a statement is run; takes a sub
619 reference. Callback is executed as $sub->($op, $info) where $op is
620 SELECT/INSERT/UPDATE/DELETE and $info is what would normally be printed.
622 See L</debugobj> for a better way.
629 if ($self->debugobj->can('callback')) {
630 return $self->debugobj->callback(@_);
636 The cursor class for this Storage object.
642 Deploy the tables to storage (CREATE TABLE and friends in a SQL-based
643 Storage class). This would normally be called through
644 L<DBIx::Class::Schema/deploy>.
648 sub deploy { die "Virtual method!" }
652 The arguments of C<connect_info> are always a single array reference,
653 and are Storage-handler specific.
655 This is normally accessed via L<DBIx::Class::Schema/connection>, which
656 encapsulates its argument list in an arrayref before calling
657 C<connect_info> here.
661 sub connect_info { die "Virtual method!" }
665 Handle a select statement.
669 sub select { die "Virtual method!" }
673 Handle an insert statement.
677 sub insert { die "Virtual method!" }
681 Handle an update statement.
685 sub update { die "Virtual method!" }
689 Handle a delete statement.
693 sub delete { die "Virtual method!" }
697 Performs a select, fetch and return of data - handles a single row
702 sub select_single { die "Virtual method!" }
704 =head2 columns_info_for
706 Returns metadata for the given source's columns. This
707 is *deprecated*, and will be removed before 1.0. You should
708 be specifying the metadata yourself if you need it.
712 sub columns_info_for { die "Virtual method!" }
714 =head1 ENVIRONMENT VARIABLES
718 If C<DBIC_TRACE> is set then trace information
719 is produced (as when the L</debug> method is set).
721 If the value is of the form C<1=/path/name> then the trace output is
722 written to the file C</path/name>.
724 This environment variable is checked when the storage object is first
725 created (when you call connect on your schema). So, run-time changes
726 to this environment variable will not take effect unless you also
727 re-connect on your schema.
729 =head2 DBIC_TRACE_PROFILE
731 If C<DBIC_TRACE_PROFILE> is set, L<DBIx::Class::Storage::Debug::PrettyPrint>
732 will be used to format the output from C<DBIC_TRACE>. The value it
733 is set to is the C<profile> that it will be used. If the value is a
734 filename the file is read with L<Config::Any> and the results are
735 used as the configuration for tracing. See L<SQL::Abstract::Tree/new>
736 for what that structure should look like.
738 =head2 DBIX_CLASS_STORAGE_DBI_DEBUG
740 Old name for DBIC_TRACE
744 L<DBIx::Class::Storage::DBI> - reference storage implementation using
745 SQL::Abstract and DBI.
747 =head1 FURTHER QUESTIONS?
749 Check the list of L<additional DBIC resources|DBIx::Class/GETTING HELP/SUPPORT>.
751 =head1 COPYRIGHT AND LICENSE
753 This module is free software L<copyright|DBIx::Class/COPYRIGHT AND LICENSE>
754 by the L<DBIx::Class (DBIC) authors|DBIx::Class/AUTHORS>. You can
755 redistribute it and/or modify it under the same terms as the
756 L<DBIx::Class library|DBIx::Class/COPYRIGHT AND LICENSE>.