__PACKAGE__->mk_group_accessors('simple' =>
qw/_connect_info _dbi_connect_info _dbh _sql_maker _sql_maker_opts
_conn_pid _conn_tid disable_sth_caching cursor on_connect_do
- transaction_depth unsafe/
+ transaction_depth unsafe _dbh_autocommit/
);
BEGIN {
ref $coderef eq 'CODE' or $self->throw_exception
('$coderef must be a CODE reference');
+ return $coderef->(@_) if $self->{transaction_depth};
+
local $self->{_in_dbh_do} = 1;
my @result;
my ($self) = @_;
if( $self->connected ) {
- $self->_dbh->rollback unless $self->_dbh->{AutoCommit};
+ $self->_dbh->rollback unless $self->_dbh_autocommit;
$self->_dbh->disconnect;
$self->_dbh(undef);
$self->{_dbh_gen}++;
# Always set the transaction depth on connect, since
# there is no transaction in progress by definition
- $self->{transaction_depth} = $self->_dbh->{AutoCommit} ? 0 : 1;
+ $self->{transaction_depth} = $self->_dbh_autocommit ? 0 : 1;
if(ref $self eq 'DBIx::Class::Storage::DBI') {
my $driver = $self->_dbh->{Driver}->{Name};
$self->throw_exception("DBI Connection failed: " . ($@||$DBI::errstr))
if !$dbh || $@;
+ $self->_dbh_autocommit($dbh->{AutoCommit});
+
$dbh;
}
sub txn_begin {
my $self = shift;
$self->ensure_connected();
- if($self->{transaction_depth}++ == 0) {
+ if($self->{transaction_depth} == 0) {
$self->debugobj->txn_begin()
if $self->debug;
# this isn't ->_dbh-> because
# for AutoCommit users
$self->dbh->begin_work;
}
+ $self->{transaction_depth}++;
}
sub txn_commit {
if ($self->debug);
$dbh->commit;
$self->{transaction_depth} = 0
- if $dbh->{AutoCommit};
+ if $self->_dbh_autocommit;
}
elsif($self->{transaction_depth} > 1) {
$self->{transaction_depth}--
sub txn_rollback {
my $self = shift;
my $dbh = $self->_dbh;
- my $autocommit;
eval {
- $autocommit = $dbh->{AutoCommit};
if ($self->{transaction_depth} == 1) {
$self->debugobj->txn_rollback()
if ($self->debug);
- $dbh->rollback;
$self->{transaction_depth} = 0
- if $autocommit;
+ if $self->_dbh_autocommit;
+ $dbh->rollback;
}
elsif($self->{transaction_depth} > 1) {
$self->{transaction_depth}--;
my $exception_class = "DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION";
$error =~ /$exception_class/ and $self->throw_exception($error);
# ensure that a failed rollback resets the transaction depth
- $self->{transaction_depth} = $autocommit ? 0 : 1;
+ $self->{transaction_depth} = $self->_dbh_autocommit ? 0 : 1;
$self->throw_exception($error);
}
}
my $schema = DBICTest->init_schema();
-plan tests => 40;
+plan tests => 54;
my $code = sub {
my ($artist, @cd_titles) = @_;
# Test successful txn_do() - scalar context
{
+ is( $schema->storage->{transaction_depth}, 0, 'txn depth starts at 0');
+
my @titles = map {'txn_do test CD ' . $_} (1..5);
my $artist = $schema->resultset('Artist')->find(1);
my $count_before = $artist->cds->count;
is($artist->cds({
title => "txn_do test CD $_",
})->first->year, 2006, "new CD $_ year correct") for (1..5);
+
+ is( $schema->storage->{transaction_depth}, 0, 'txn depth has been reset');
}
# Test successful txn_do() - list context
{
+ is( $schema->storage->{transaction_depth}, 0, 'txn depth starts at 0');
+
my @titles = map {'txn_do test CD ' . $_} (6..10);
my $artist = $schema->resultset('Artist')->find(1);
my $count_before = $artist->cds->count;
is($artist->cds({
title => "txn_do test CD $_",
})->first->year, 2006, "new CD $_ year correct") for (6..10);
+
+ is( $schema->storage->{transaction_depth}, 0, 'txn depth has been reset');
}
# Test nested successful txn_do()
{
+ is( $schema->storage->{transaction_depth}, 0, 'txn depth starts at 0');
+
my $nested_code = sub {
my ($schema, $artist, $code) = @_;
title => 'nested txn_do test CD '.$_,
})->first->year, 2006, qq{nested txn_do CD$_ year ok}) for (1..10);
is($artist->cds->count, $count_before+10, 'nested txn_do added all CDs');
+
+ is( $schema->storage->{transaction_depth}, 0, 'txn depth has been reset');
}
my $fail_code = sub {
# Test failed txn_do()
{
+
+ is( $schema->storage->{transaction_depth}, 0, 'txn depth starts at 0');
+
+ my $artist = $schema->resultset('Artist')->find(3);
+
+ eval {
+ $schema->txn_do($fail_code, $artist);
+ };
+
+ my $error = $@;
+
+ like($error, qr/the sky is falling/, 'failed txn_do threw an exception');
+ my $cd = $artist->cds({
+ title => 'this should not exist',
+ year => 2005,
+ })->first;
+ ok(!defined($cd), q{failed txn_do didn't change the cds table});
+
+ is( $schema->storage->{transaction_depth}, 0, 'txn depth has been reset');
+}
+
+# do the same transaction again
+{
+ is( $schema->storage->{transaction_depth}, 0, 'txn depth starts at 0');
+
my $artist = $schema->resultset('Artist')->find(3);
eval {
year => 2005,
})->first;
ok(!defined($cd), q{failed txn_do didn't change the cds table});
+
+ is( $schema->storage->{transaction_depth}, 0, 'txn depth has been reset');
}
# Test failed txn_do() with failed rollback
{
+ is( $schema->storage->{transaction_depth}, 0, 'txn depth starts at 0');
+
my $artist = $schema->resultset('Artist')->find(3);
# Force txn_rollback() to throw an exception
no warnings 'redefine';
no strict 'refs';
- local *{"DBIx::Class::Storage::DBI::SQLite::txn_rollback"} = sub{die 'FAILED'};
+
+ # die in rollback, but maintain sanity for further tests ...
+ local *{"DBIx::Class::Storage::DBI::SQLite::txn_rollback"} = sub{
+ my $storage = shift;
+ $storage->{transaction_depth}--;
+ die 'FAILED';
+ };
eval {
$schema->txn_do($fail_code, $artist);
year => 2005,
})->first;
ok(!defined($cd), q{deleted the failed txn's cd});
- $schema->storage->{transaction_depth} = 0; # Must reset this or further tests
- # will fail
+ $schema->storage->_dbh->rollback;
}
# Test nested failed txn_do()
{
+ is( $schema->storage->{transaction_depth}, 0, 'txn depth starts at 0');
+
my $nested_fail_code = sub {
my ($schema, $artist, $code1, $code2) = @_;