X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FStorage%2FDBI.pm;h=d9063c75137a6798899ad223b47e9e176888da53;hb=1c6a29fecdd74b402851da5678d78a200afc9e11;hp=8c991a1031cbbab100235f89ea792dabdb740884;hpb=d6feb60f4c904b02c0f4367f0aca5d9dc8455f83;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/Storage/DBI.pm b/lib/DBIx/Class/Storage/DBI.pm index 8c991a1..d9063c7 100644 --- a/lib/DBIx/Class/Storage/DBI.pm +++ b/lib/DBIx/Class/Storage/DBI.pm @@ -5,6 +5,7 @@ use base 'DBIx::Class::Storage'; use strict; use warnings; +use Carp::Clan qw/^DBIx::Class/; use DBI; use SQL::Abstract::Limit; use DBIx::Class::Storage::DBI::Cursor; @@ -15,7 +16,7 @@ __PACKAGE__->mk_group_accessors('simple' => qw/_connect_info _dbi_connect_info _dbh _sql_maker _sql_maker_opts _conn_pid _conn_tid disable_sth_caching on_connect_do on_disconnect_do transaction_depth unsafe _dbh_autocommit - auto_savepoint/ + auto_savepoint savepoints/ ); __PACKAGE__->cursor_class('DBIx::Class::Storage::DBI::Cursor'); @@ -25,7 +26,8 @@ __PACKAGE__->sql_maker_class('DBIC::SQL::Abstract'); BEGIN { -package DBIC::SQL::Abstract; # Would merge upstream, but nate doesn't reply :( +package # Hide from PAUSE + DBIC::SQL::Abstract; # Would merge upstream, but nate doesn't reply :( use base qw/SQL::Abstract::Limit/; @@ -328,6 +330,7 @@ sub new { $new->transaction_depth(0); $new->_sql_maker_opts({}); + $new->{savepoints} = []; $new->{_in_dbh_do} = 0; $new->{_dbh_gen} = 0; @@ -430,6 +433,12 @@ Note that your custom settings can cause Storage to malfunction, especially if you set a C handler that suppresses exceptions and/or disable C. +=item auto_savepoint + +If this option is true, L will use savepoints when nesting +transactions, making it possible to recover from failure in the inner +transaction without having to abort all outer transactions. + =back These options can be mixed in with your other L connection attributes, @@ -591,7 +600,7 @@ sub dbh_do { eval { $self->_verify_pid if $dbh; - if( !$dbh ) { + if(!$self->_dbh) { $self->_populate_dbh; $dbh = $self->_dbh; } @@ -871,56 +880,87 @@ sub _connect { sub svp_begin { my ($self, $name) = @_; - - $self->throw_exception("You failed to provide a savepoint name!") if !$name; - if($self->{transaction_depth} == 0) { - warn("Can't use savepoints without a transaction."); - return 0; - } + $name = $self->_svp_generate_name + unless defined $name; + + $self->throw_exception ("You can't use savepoints outside a transaction") + if $self->{transaction_depth} == 0; + + $self->throw_exception ("Your Storage implementation doesn't support savepoints") + unless $self->can('_svp_begin'); + + push @{ $self->{savepoints} }, $name; - if(!$self->can('_svp_begin')) { - warn("Your Storage implementation doesn't support savepoints!"); - return 0; - } $self->debugobj->svp_begin($name) if $self->debug; - $self->_svp_begin($name); + + return $self->_svp_begin($name); } sub svp_release { my ($self, $name) = @_; - $self->throw_exception("You failed to provide a savepoint name!") if !$name; + $self->throw_exception ("You can't use savepoints outside a transaction") + if $self->{transaction_depth} == 0; - if($self->{transaction_depth} == 0) { - warn("Can't use savepoints without a transaction."); - return 0; - } + $self->throw_exception ("Your Storage implementation doesn't support savepoints") + unless $self->can('_svp_release'); - if(!$self->can('_svp_release')) { - warn("Your Storage implementation doesn't support savepoint releasing!"); - return 0; + if (defined $name) { + $self->throw_exception ("Savepoint '$name' does not exist") + unless grep { $_ eq $name } @{ $self->{savepoints} }; + + # Dig through the stack until we find the one we are releasing. This keeps + # the stack up to date. + my $svp; + + do { $svp = pop @{ $self->{savepoints} } } while $svp ne $name; + } else { + $name = pop @{ $self->{savepoints} }; } + $self->debugobj->svp_release($name) if $self->debug; - $self->_svp_release($name); + + return $self->_svp_release($name); } sub svp_rollback { my ($self, $name) = @_; - $self->throw_exception("You failed to provide a savepoint name!") if !$name; + $self->throw_exception ("You can't use savepoints outside a transaction") + if $self->{transaction_depth} == 0; - if($self->{transaction_depth} == 0) { - warn("Can't use savepoints without a transaction."); - return 0; - } + $self->throw_exception ("Your Storage implementation doesn't support savepoints") + unless $self->can('_svp_rollback'); - if(!$self->can('_svp_rollback')) { - warn("Your Storage implementation doesn't support savepoints!"); - return 0; + if (defined $name) { + # If they passed us a name, verify that it exists in the stack + unless(grep({ $_ eq $name } @{ $self->{savepoints} })) { + $self->throw_exception("Savepoint '$name' does not exist!"); + } + + # Dig through the stack until we find the one we are releasing. This keeps + # the stack up to date. + while(my $s = pop(@{ $self->{savepoints} })) { + last if($s eq $name); + } + # Add the savepoint back to the stack, as a rollback doesn't remove the + # named savepoint, only everything after it. + push(@{ $self->{savepoints} }, $name); + } else { + # We'll assume they want to rollback to the last savepoint + $name = $self->{savepoints}->[-1]; } + $self->debugobj->svp_rollback($name) if $self->debug; - $self->_svp_rollback($name); + + return $self->_svp_rollback($name); +} + +sub _svp_generate_name { + my ($self) = @_; + + return 'savepoint_'.scalar(@{ $self->{'savepoints'} }); } sub txn_begin { @@ -934,7 +974,7 @@ sub txn_begin { # for AutoCommit users $self->dbh->begin_work; } elsif ($self->auto_savepoint) { - $self->svp_begin ("savepoint_$self->{transaction_depth}"); + $self->svp_begin; } $self->{transaction_depth}++; } @@ -951,7 +991,7 @@ sub txn_commit { } elsif($self->{transaction_depth} > 1) { $self->{transaction_depth}--; - $self->svp_release ("savepoint_$self->{transaction_depth}") + $self->svp_release if $self->auto_savepoint; } } @@ -970,8 +1010,8 @@ sub txn_rollback { elsif($self->{transaction_depth} > 1) { $self->{transaction_depth}--; if ($self->auto_savepoint) { - $self->svp_rollback ("savepoint_$self->{transaction_depth}"); - $self->svp_release ("savepoint_$self->{transaction_depth}"); + $self->svp_rollback; + $self->svp_release; } } else { @@ -1023,6 +1063,7 @@ sub _query_start { if ( $self->debug ) { @bind = $self->_fix_bind_params(@bind); + $self->debugobj->query_start( $sql, @bind ); } } @@ -1247,11 +1288,24 @@ sub select_single { my $self = shift; my ($rv, $sth, @bind) = $self->_select(@_); my @row = $sth->fetchrow_array; + if(@row && $sth->fetchrow_array) { + carp "Query returned more than one row. SQL that returns multiple rows is DEPRECATED for ->find and ->single"; + } # Need to call finish() to work round broken DBDs $sth->finish(); return @row; } +sub reload_row { + my ($self, $row) = @_; + + my $reload = $row->result_source->resultset->find( + map { $row->$_ } $row->primary_columns + ); + + return $reload; +} + =head2 sth =over 4 @@ -1389,13 +1443,22 @@ sub bind_attribute_by_data_type { =over 4 -=item Arguments: $schema \@databases, $version, $directory, $preversion, $sqlt_args +=item Arguments: $schema \@databases, $version, $directory, $preversion, \%sqlt_args =back Creates a SQL file based on the Schema, for each of the specified database types, in the given directory. +By default, C<\%sqlt_args> will have + + { add_drop_table => 1, ignore_constraint_names => 1, ignore_index_names => 1 } + +merged with the hash passed in. To disable any of those features, pass in a +hashref like the following + + { ignore_constraint_names => 0, # ... other options } + =cut sub create_ddl_dir @@ -1410,7 +1473,12 @@ sub create_ddl_dir $databases ||= ['MySQL', 'SQLite', 'PostgreSQL']; $databases = [ $databases ] if(ref($databases) ne 'ARRAY'); $version ||= $schema->VERSION || '1.x'; - $sqltargs = { ( add_drop_table => 1 ), %{$sqltargs || {}} }; + $sqltargs = { + add_drop_table => 1, + ignore_constraint_names => 1, + ignore_index_names => 1, + %{$sqltargs || {}} + }; $self->throw_exception(q{Can't create a ddl file without SQL::Translator 0.09: '} . $self->_check_sqlt_message . q{'}) @@ -1663,6 +1731,31 @@ sub build_datetime_parser { } } +=head2 is_replicating + +A boolean that reports if a particular L is set to +replicate from a master database. Default is undef, which is the result +returned by databases that don't support replication. + +=cut + +sub is_replicating { + return; + +} + +=head2 lag_behind_master + +Returns a number that represents a certain amount of lag behind a master db +when a given storage is replicating. The number is database dependent, but +starts at zero and increases with the amount of lag. Default in undef + +=cut + +sub lag_behind_master { + return; +} + sub DESTROY { my $self = shift; return if !$self->_dbh;