DBIx-Class
view release on metacpan or search on metacpan
lib/DBIx/Class/Storage/TxnScopeGuard.pm view on Meta::CPAN
package DBIx::Class::Storage::TxnScopeGuard;
use strict;
use warnings;
use Try::Tiny;
use Scalar::Util qw(weaken blessed refaddr);
use DBIx::Class;
use DBIx::Class::_Util qw(is_exception detected_reinvoked_destructor);
use DBIx::Class::Carp;
use namespace::clean;
sub new {
my ($class, $storage) = @_;
my $guard = {
inactivated => 0,
storage => $storage,
};
# we are starting with an already set $@ - in order for things to work we need to
# be able to recognize it upon destruction - store its weakref
# recording it before doing the txn_begin stuff
#
# FIXME FRAGILE - any eval that fails but *does not* rethrow between here
# and the unwind will trample over $@ and invalidate the entire mechanism
# There got to be a saner way of doing this...
if (is_exception $@) {
weaken(
$guard->{existing_exception_ref} = (ref($@) eq '') ? \$@ : $@
);
}
$storage->txn_begin;
weaken( $guard->{dbh} = $storage->_dbh );
bless $guard, ref $class || $class;
$guard;
}
sub commit {
my $self = shift;
$self->{storage}->throw_exception("Refusing to execute multiple commits on scope guard $self")
if $self->{inactivated};
# FIXME - this assumption may be premature: a commit may fail and a rollback
# *still* be necessary. Currently I am not aware of such scenarious, but I
# also know the deferred constraint handling is *severely* undertested.
# Making the change of "fire txn and never come back to this" in order to
# address RT#107159, but this *MUST* be reevaluated later.
$self->{inactivated} = 1;
$self->{storage}->txn_commit;
}
sub DESTROY {
return if &detected_reinvoked_destructor;
my $self = shift;
return if $self->{inactivated};
# if our dbh is not ours anymore, the $dbh weakref will go undef
$self->{storage}->_verify_pid unless DBIx::Class::_ENV_::BROKEN_FORK;
return unless $self->{dbh};
my $exception = $@ if (
is_exception $@
and
(
! defined $self->{existing_exception_ref}
or
refaddr( ref($@) eq '' ? \$@ : $@ ) != refaddr($self->{existing_exception_ref})
)
);
{
local $@;
carp 'A DBIx::Class::Storage::TxnScopeGuard went out of scope without explicit commit or error. Rolling back.'
unless defined $exception;
my $rollback_exception;
# do minimal connectivity check due to weird shit like
# https://rt.cpan.org/Public/Bug/Display.html?id=62370
try { $self->{storage}->_seems_connected && $self->{storage}->txn_rollback }
catch { $rollback_exception = shift };
if ( $rollback_exception and (
! defined blessed $rollback_exception
or
! $rollback_exception->isa('DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION')
) ) {
# append our text - THIS IS A TEMPORARY FIXUP!
( run in 0.694 second using v1.01-cache-2.11-cpan-39bf76dae61 )