From: Peter Rabbitson Date: Sat, 6 Mar 2010 23:52:50 +0000 (+0000) Subject: Cascading delete needs a guard to remain atomic X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=fb61e30ca4a0670462445ed8c376eccdfe68ca71;p=dbsrgits%2FDBIx-Class-Historic.git Cascading delete needs a guard to remain atomic --- diff --git a/Changes b/Changes index d4bced8..791d918 100644 --- a/Changes +++ b/Changes @@ -1,6 +1,8 @@ Revision history for DBIx::Class - DBIx::Class::InflateColumn::File entered deprecated state + - Cascading delete/update are now wrapped in a transaction + for atomicity - Fix regression where SQL files with comments were not handled properly by ::Schema::Versioned. - Fix regression on not properly throwing when $obj->relationship diff --git a/lib/DBIx/Class/Relationship/CascadeActions.pm b/lib/DBIx/Class/Relationship/CascadeActions.pm index c3a66ea..fde8f5d 100644 --- a/lib/DBIx/Class/Relationship/CascadeActions.pm +++ b/lib/DBIx/Class/Relationship/CascadeActions.pm @@ -16,15 +16,24 @@ sub delete { # be handling this anyway. Assuming we have joins we probably actually # *could* do them, but I'd rather not. - my $ret = $self->next::method(@rest); - my $source = $self->result_source; my %rels = map { $_ => $source->relationship_info($_) } $source->relationships; my @cascade = grep { $rels{$_}{attrs}{cascade_delete} } keys %rels; - foreach my $rel (@cascade) { - $self->search_related($rel)->delete_all; + + if (@cascade) { + my $guard = $source->schema->txn_scope_guard; + + my $ret = $self->next::method(@rest); + + foreach my $rel (@cascade) { + $self->search_related($rel)->delete_all; + } + + $guard->commit; + return $ret; } - return $ret; + + $self->next::method(@rest); } sub update { @@ -32,22 +41,31 @@ sub update { return $self->next::method(@rest) unless ref $self; # Because update cascades on a class *really* don't make sense! - my $ret = $self->next::method(@rest); - my $source = $self->result_source; my %rels = map { $_ => $source->relationship_info($_) } $source->relationships; my @cascade = grep { $rels{$_}{attrs}{cascade_update} } keys %rels; - foreach my $rel (@cascade) { - next if ( - $rels{$rel}{attrs}{accessor} - && - $rels{$rel}{attrs}{accessor} eq 'single' - && - !exists($self->{_relationship_data}{$rel}) - ); - $_->update for grep defined, $self->$rel; + + if (@cascade) { + my $guard = $source->schema->txn_scope_guard; + + my $ret = $self->next::method(@rest); + + foreach my $rel (@cascade) { + next if ( + $rels{$rel}{attrs}{accessor} + && + $rels{$rel}{attrs}{accessor} eq 'single' + && + !exists($self->{_relationship_data}{$rel}) + ); + $_->update for grep defined, $self->$rel; + } + + $guard->commit; + return $ret; } - return $ret; + + $self->next::method(@rest); } 1; diff --git a/t/81transactions.t b/t/81transactions.t index 2a592e1..a13c651 100644 --- a/t/81transactions.t +++ b/t/81transactions.t @@ -150,10 +150,9 @@ my $fail_code = sub { no warnings 'redefine'; no strict 'refs'; - # die in rollback, but maintain sanity for further tests ... + # die in rollback local *{"DBIx::Class::Storage::DBI::SQLite::txn_rollback"} = sub{ my $storage = shift; - $storage->{transaction_depth}--; die 'FAILED'; }; @@ -180,6 +179,9 @@ my $fail_code = sub { $schema->storage->_dbh->rollback; } +# reset schema object (the txn_rollback meddling screws it up) +$schema = DBICTest->init_schema(); + # Test nested failed txn_do() { is( $schema->storage->{transaction_depth}, 0, 'txn depth starts at 0');