use warnings;
use base 'DBIx::Class';
+use mro 'c3';
use DBIx::Class::Carp;
use Try::Tiny;
use Scalar::Util qw/weaken blessed/;
-use DBIx::Class::_Util qw(refcount quote_sub);
+use DBIx::Class::_Util qw(
+ refcount quote_sub scope_guard
+ is_exception dbic_internal_try
+ fail_on_internal_call
+);
use Devel::GlobalDestruction;
use namespace::clean;
-__PACKAGE__->mk_classdata('class_mappings' => {});
-__PACKAGE__->mk_classdata('source_registrations' => {});
-__PACKAGE__->mk_classdata('storage_type' => '::DBI');
-__PACKAGE__->mk_classdata('storage');
-__PACKAGE__->mk_classdata('exception_action');
-__PACKAGE__->mk_classdata('stacktrace' => $ENV{DBIC_TRACE} || 0);
-__PACKAGE__->mk_classdata('default_resultset_attributes' => {});
+__PACKAGE__->mk_group_accessors( inherited => qw( storage exception_action ) );
+__PACKAGE__->mk_classaccessor('class_mappings' => {});
+__PACKAGE__->mk_classaccessor('source_registrations' => {});
+__PACKAGE__->mk_classaccessor('storage_type' => '::DBI');
+__PACKAGE__->mk_classaccessor('stacktrace' => $ENV{DBIC_TRACE} || 0);
+__PACKAGE__->mk_classaccessor('default_resultset_attributes' => {});
=head1 NAME
my $me = shift;
my $rs_class = ref ($_[0]) || $_[0];
- return try {
+ return dbic_internal_try {
$rs_class->result_source_instance
} catch {
$me->throw_exception (
=cut
-sub connect { shift->clone->connection(@_) }
+sub connect {
+ DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS and fail_on_internal_call;
+ shift->clone->connection(@_);
+}
=head2 resultset
=cut
sub populate {
+ DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_INDIRECT_CALLS and fail_on_internal_call;
+
my ($self, $name, $data) = @_;
my $rs = $self->resultset($name)
or $self->throw_exception("'$name' is not a resultset");
$storage_class =~ s/^::/DBIx::Class::Storage::/;
- try {
+ dbic_internal_try {
$self->ensure_class_loaded ($storage_class);
}
catch {
=cut
-# this might be oversimplified
-# sub compose_namespace {
-# my ($self, $target, $base) = @_;
-
-# my $schema = $self->clone;
-# foreach my $source_name ($schema->sources) {
-# my $source = $schema->source($source_name);
-# my $target_class = "${target}::${source_name}";
-# $self->inject_base(
-# $target_class => $source->result_class, ($base ? $base : ())
-# );
-# $source->result_class($target_class);
-# $target_class->result_source_instance($source)
-# if $target_class->can('result_source_instance');
-# $schema->register_source($source_name, $source);
-# }
-# return $schema;
-# }
-
sub compose_namespace {
my ($self, $target, $base) = @_;
}
}
+ # Legacy stuff, not inserting INDIRECT assertions
quote_sub "${target}::${_}" => "shift->schema->$_(\@_)"
for qw(class source resultset);
}
=cut
sub throw_exception {
- my $self = shift;
+ my ($self, @args) = @_;
+
+ if (
+ ! DBIx::Class::_Util::in_internal_try()
+ and
+ my $act = $self->exception_action
+ ) {
+
+ my $guard_disarmed;
+
+ my $guard = scope_guard {
+ return if $guard_disarmed;
+ local $SIG{__WARN__};
+ Carp::cluck("
+ !!! DBIx::Class INTERNAL PANIC !!!
+
+The exception_action() handler installed on '$self'
+aborted the stacktrace below via a longjmp (either via Return::Multilevel or
+plain goto, or Scope::Upper or something equally nefarious). There currently
+is nothing safe DBIx::Class can do, aside from displaying this error. A future
+version ( 0.082900, when available ) will reduce the cases in which the
+handler is invoked, but this is neither a complete solution, nor can it do
+anything for other software that might be affected by a similar problem.
+
+ !!! FIX YOUR ERROR HANDLING !!!
+
+This guard was activated beginning"
+ );
+ };
- if (my $act = $self->exception_action) {
- if ($act->(@_)) {
- DBIx::Class::Exception->throw(
+ dbic_internal_try {
+ # if it throws - good, we'll assign to @args in the end
+ # if it doesn't - do different things depending on RV truthiness
+ if( $act->(@args) ) {
+ $args[0] = (
"Invocation of the exception_action handler installed on $self did *not*"
.' result in an exception. DBIx::Class is unable to function without a reliable'
- .' exception mechanism, ensure that exception_action does not hide exceptions'
- ." (original error: $_[0])"
- );
+ .' exception mechanism, ensure your exception_action does not hide exceptions'
+ ." (original error: $args[0])"
+ );
+ }
+ else {
+ carp_unique (
+ "The exception_action handler installed on $self returned false instead"
+ .' of throwing an exception. This behavior has been deprecated, adjust your'
+ .' handler to always rethrow the supplied error'
+ );
+ }
+
+ 1;
}
+ catch {
+ # We call this to get the necessary warnings emitted and disregard the RV
+ # as it's definitely an exception if we got as far as this catch{} block
+ is_exception(
+ $args[0] = $_
+ );
+ };
- carp_unique (
- "The exception_action handler installed on $self returned false instead"
- .' of throwing an exception. This behavior has been deprecated, adjust your'
- .' handler to always rethrow the supplied error.'
- );
+ # Done guarding against https://github.com/PerlDancer/Dancer2/issues/1125
+ $guard_disarmed = 1;
}
- DBIx::Class::Exception->throw($_[0], $self->stacktrace);
+ DBIx::Class::Exception->throw( $args[0], $self->stacktrace );
}
=head2 deploy
sub ddl_filename {
my ($self, $type, $version, $dir, $preversion) = @_;
- require File::Spec;
-
$version = "$preversion-$version" if $preversion;
my $class = blessed($self) || $self;
$class =~ s/::/-/g;
- return File::Spec->catfile($dir, "$class-$version-$type.sql");
+ return "$dir/$class-$version-$type.sql";
}
=head2 thaw
return $source if $params->{extra};
my $rs_class = $source->result_class;
- if ($rs_class and my $rsrc = try { $rs_class->result_source_instance } ) {
+ if ($rs_class and my $rsrc = dbic_internal_try { $rs_class->result_source_instance } ) {
my %map = %{$self->class_mappings};
if (
exists $map{$rs_class}
my $global_phase_destroy;
sub DESTROY {
- ### NO detect_reinvoked_destructor check
+ ### NO detected_reinvoked_destructor check
### This code very much relies on being called multuple times
return if $global_phase_destroy ||= in_global_destruction;
# however beware - on older perls the exception seems randomly untrappable
# due to some weird race condition during thread joining :(((
if (length ref $srcs->{$source_name} and refcount($srcs->{$source_name}) > 1) {
+ local $SIG{__DIE__} if $SIG{__DIE__};
local $@;
eval {
$srcs->{$source_name}->schema($self);
last;
}
}
+
+ # Dummy NEXTSTATE ensuring the all temporaries on the stack are garbage
+ # collected before leaving this scope. Depending on the code above, this
+ # may very well be just a preventive measure guarding future modifications
+ undef;
}
sub _unregister_source {
carp_once "compose_connection deprecated as of 0.08000"
unless $INC{"DBIx/Class/CDBICompat.pm"};
- try {
+ dbic_internal_try {
require DBIx::Class::ResultSetProxy;
}
catch {
my $source = $self->source($source_name);
my $class = $source->result_class;
$self->inject_base($class, 'DBIx::Class::ResultSetProxy');
- $class->mk_classdata(resultset_instance => $source->resultset);
- $class->mk_classdata(class_resolver => $self);
+ $class->mk_classaccessor(resultset_instance => $source->resultset);
+ $class->mk_classaccessor(class_resolver => $self);
}
$self->connection(@info);
return $self;
my $source = $schema->source($source_name);
my $class = $source->result_class;
#warn "$source_name $class $source ".$source->storage;
- $class->mk_classdata(result_source_instance => $source);
- $class->mk_classdata(resultset_instance => $source->resultset);
- $class->mk_classdata(class_resolver => $schema);
+ $class->mk_classaccessor(result_source_instance => $source);
+ $class->mk_classaccessor(resultset_instance => $source->resultset);
+ $class->mk_classaccessor(class_resolver => $schema);
}
return $schema;
}