X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FStorage.pm;h=0575c32fbf24b813c069fb8c393bcfc1da855e86;hb=9780718f9c36738245f90b1f036998c3b076cffc;hp=e422da47977c665bce07f1b4a75b759f14b2f2ed;hpb=aaba95249e9b4a986ff3f5820e6c95e2ecae68f9;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/Storage.pm b/lib/DBIx/Class/Storage.pm index e422da4..0575c32 100644 --- a/lib/DBIx/Class/Storage.pm +++ b/lib/DBIx/Class/Storage.pm @@ -4,12 +4,19 @@ use strict; use warnings; use base qw/DBIx::Class/; +use mro 'c3'; -use Scalar::Util qw/weaken/; -use Carp::Clan qw/^DBIx::Class/; +use DBIx::Class::Exception; +use Scalar::Util(); +use IO::File; +use DBIx::Class::Storage::TxnScopeGuard; -__PACKAGE__->load_components(qw/AccessorGroup/); __PACKAGE__->mk_group_accessors('simple' => qw/debug debugobj schema/); +__PACKAGE__->mk_group_accessors('inherited' => 'cursor_class'); + +__PACKAGE__->cursor_class('DBIx::Class::Cursor'); + +sub cursor { shift->cursor_class(@_); } package # Hide from PAUSE DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION; @@ -56,19 +63,11 @@ sub new { $new->set_schema($schema); $new->debugobj(new DBIx::Class::Storage::Statistics()); - my $fh; + #my $fh; my $debug_env = $ENV{DBIX_CLASS_STORAGE_DBI_DEBUG} || $ENV{DBIC_TRACE}; - if (defined($debug_env) && ($debug_env =~ /=(.+)$/)) { - $fh = IO::File->new($1, 'w') - or $new->throw_exception("Cannot open trace file $1"); - } else { - $fh = IO::File->new('>&STDERR'); - } - - $new->debugfh($fh); $new->debug(1) if $debug_env; $new; @@ -84,7 +83,7 @@ storage object, such as during L. sub set_schema { my ($self, $schema) = @_; $self->schema($schema); - weaken($self->{schema}) if ref $self->{schema}; + Scalar::Util::weaken($self->{schema}) if ref $self->{schema}; } =head2 connected @@ -121,8 +120,12 @@ Throws an exception - croaks. sub throw_exception { my $self = shift; - $self->schema->throw_exception(@_) if $self->schema; - croak @_; + if ($self->schema) { + $self->schema->throw_exception(@_); + } + else { + DBIx::Class::Exception->throw(@_); + } } =head2 txn_do @@ -155,21 +158,30 @@ For example, }; my $rs; - eval { + try { $rs = $schema->txn_do($coderef); - }; - - if ($@) { # Transaction failed + } catch { + my $error = shift; + # Transaction failed die "something terrible has happened!" # - if ($@ =~ /Rollback failed/); # Rollback failed + if ($error =~ /Rollback failed/); # Rollback failed deal_with_failed_transaction(); - } + }; In a nested transaction (calling txn_do() from within a txn_do() coderef) only the outermost transaction will issue a L, and txn_do() can be called in void, scalar and list context and it will behave as expected. +Please note that all of the code in your coderef, including non-DBIx::Class +code, is part of a transaction. This transaction may fail out halfway, or +it may get partially double-executed (in the case that our DB connection +failed halfway through the transaction, in which case we reconnect and +restart the txn). Therefore it is best that any side-effects in your coderef +are idempotent (that is, can be re-executed multiple times and get the +same result), and that you check up on your side-effects in the case of +transaction failure. + =cut sub txn_do { @@ -183,9 +195,9 @@ sub txn_do { $self->txn_begin; # If this throws an exception, no rollback is needed my $wantarray = wantarray; # Need to save this since the context - # inside the eval{} block is independent + # inside the try{} block is independent # of the context that called txn_do() - eval { + try { # Need to differentiate between scalar/list context to allow for # returning a list in scalar context to get the size of the list @@ -200,17 +212,13 @@ sub txn_do { $coderef->(@args); } $self->txn_commit; - }; + } catch { + my $error = shift; - if ($@) { - my $error = $@; - - eval { + try { $self->txn_rollback; - }; - - if ($@) { - my $rollback_error = $@; + } catch { + my $rollback_error = shift; my $exception_class = "DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION"; $self->throw_exception($error) # propagate nested rollback if $rollback_error =~ /$exception_class/; @@ -218,9 +226,8 @@ sub txn_do { $self->throw_exception( "Transaction aborted: $error. Rollback failed: ${rollback_error}" ); - } else { - $self->throw_exception($error); # txn failed but rollback succeeded } + $self->throw_exception($error); # txn failed but rollback succeeded } return $wantarray ? @return_values : $return_value; @@ -241,6 +248,9 @@ sub txn_begin { die "Virtual method!" } Issues a commit of the current transaction. +It does I perform an actual storage commit unless there's a DBIx::Class +transaction currently in effect (i.e. you called L). + =cut sub txn_commit { die "Virtual method!" } @@ -255,10 +265,73 @@ which allows the rollback to propagate to the outermost transaction. sub txn_rollback { die "Virtual method!" } +=head2 svp_begin + +Arguments: $savepoint_name? + +Created a new savepoint using the name provided as argument. If no name +is provided, a random name will be used. + +=cut + +sub svp_begin { die "Virtual method!" } + +=head2 svp_release + +Arguments: $savepoint_name? + +Release the savepoint provided as argument. If none is provided, +release the savepoint created most recently. This will implicitly +release all savepoints created after the one explicitly released as well. + +=cut + +sub svp_release { die "Virtual method!" } + +=head2 svp_rollback + +Arguments: $savepoint_name? + +Rollback to the savepoint provided as argument. If none is provided, +rollback to the savepoint created most recently. This will implicitly +release all savepoints created after the savepoint we rollback to. + +=cut + +sub svp_rollback { die "Virtual method!" } + +=for comment + +=head2 txn_scope_guard + +An alternative way of transaction handling based on +L: + + my $txn_guard = $storage->txn_scope_guard; + + $row->col1("val1"); + $row->update; + + $txn_guard->commit; + +If an exception occurs, or the guard object otherwise leaves the scope +before C<< $txn_guard->commit >> is called, the transaction will be rolled +back by an explicit L call. In essence this is akin to +using a L/L pair, without having to worry +about calling L at the right places. Note that since there +is no defined code closure, there will be no retries and other magic upon +database disconnection. If you need such functionality see L. + +=cut + +sub txn_scope_guard { + return DBIx::Class::Storage::TxnScopeGuard->new($_[0]); +} + =head2 sql_maker Returns a C object - normally an object of class -C. +C. =cut @@ -275,7 +348,7 @@ shell environment. =head2 debugfh Set or retrieve the filehandle used for trace/debug output. This should be -an IO::Handle compatible ojbect (only the C method is used. Initially +an IO::Handle compatible object (only the C method is used. Initially set to be STDERR - although see information on the L environment variable. @@ -314,14 +387,12 @@ sub debugcb { } } -=head2 cursor +=head2 cursor_class The cursor class for this Storage object. =cut -sub cursor { die "Virtual method!" } - =head2 deploy Deploy the tables to storage (CREATE TABLE and friends in a SQL-based @@ -388,7 +459,9 @@ sub select_single { die "Virtual method!" } =head2 columns_info_for -Returns database type info for the given table's columns. +Returns metadata for the given source's columns. This +is *deprecated*, and will be removed before 1.0. You should +be specifying the metadata yourself if you need it. =cut @@ -413,6 +486,11 @@ re-connect on your schema. Old name for DBIC_TRACE +=head1 SEE ALSO + +L - reference storage implementation using +SQL::Abstract and DBI. + =head1 AUTHORS Matt S. Trout