X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FSchema.pm;h=c0cba10c2c4d3651b7debd54f78300821df37b99;hb=e5053694;hp=a9b312bca2ecdb4a35dc24449d22459446a5a448;hpb=a2bd379666d729133d65c85dc775627937084b18;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/Schema.pm b/lib/DBIx/Class/Schema.pm index a9b312b..c0cba10 100644 --- a/lib/DBIx/Class/Schema.pm +++ b/lib/DBIx/Class/Schema.pm @@ -4,21 +4,25 @@ use strict; 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 @@ -191,7 +195,7 @@ sub _ns_get_rsrc_instance { my $me = shift; my $rs_class = ref ($_[0]) || $_[0]; - return try { + return dbic_internal_try { $rs_class->result_source_instance } catch { $me->throw_exception ( @@ -521,7 +525,10 @@ version, overload L instead. =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 @@ -765,6 +772,8 @@ those values. =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"); @@ -803,7 +812,7 @@ sub connection { $storage_class =~ s/^::/DBIx::Class::Storage::/; - try { + dbic_internal_try { $self->ensure_class_loaded ($storage_class); } catch { @@ -862,25 +871,6 @@ will produce the output =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) = @_; @@ -1055,26 +1045,70 @@ default behavior will provide a detailed stack trace. =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; - if (my $act = $self->exception_action) { - if ($act->(@_)) { - DBIx::Class::Exception->throw( + 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" + ); + }; + + 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 @@ -1192,14 +1226,12 @@ format. 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 @@ -1213,7 +1245,6 @@ reference to any schema, so are rather useless. sub thaw { my ($self, $obj) = @_; local $DBIx::Class::ResultSourceHandle::thaw_schema = $self; - require Storable; return Storable::thaw($obj); } @@ -1225,7 +1256,6 @@ it is just provided here for symmetry. =cut sub freeze { - require Storable; return Storable::nfreeze($_[1]); } @@ -1248,7 +1278,6 @@ objects so their references to the schema object sub dclone { my ($self, $obj) = @_; local $DBIx::Class::ResultSourceHandle::thaw_schema = $self; - require Storable; return Storable::dclone($obj); } @@ -1363,7 +1392,7 @@ sub _register_source { 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} @@ -1388,6 +1417,9 @@ sub _register_source { my $global_phase_destroy; sub DESTROY { + ### NO detected_reinvoked_destructor check + ### This code very much relies on being called multuple times + return if $global_phase_destroy ||= in_global_destruction; my $self = shift; @@ -1402,6 +1434,7 @@ sub DESTROY { # 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); @@ -1414,6 +1447,11 @@ sub DESTROY { 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 { @@ -1470,13 +1508,12 @@ sub compose_connection { carp_once "compose_connection deprecated as of 0.08000" unless $INC{"DBIx/Class/CDBICompat.pm"}; - my $base = 'DBIx::Class::ResultSetProxy'; - try { - eval "require ${base};" + dbic_internal_try { + require DBIx::Class::ResultSetProxy; } catch { $self->throw_exception - ("No arguments to load_classes and couldn't load ${base} ($_)") + ("No arguments to load_classes and couldn't load DBIx::Class::ResultSetProxy ($_)") }; if ($self eq $target) { @@ -1484,15 +1521,15 @@ sub compose_connection { foreach my $source_name ($self->sources) { my $source = $self->source($source_name); my $class = $source->result_class; - $self->inject_base($class, $base); - $class->mk_classdata(resultset_instance => $source->resultset); - $class->mk_classdata(class_resolver => $self); + $self->inject_base($class, 'DBIx::Class::ResultSetProxy'); + $class->mk_classaccessor(resultset_instance => $source->resultset); + $class->mk_classaccessor(class_resolver => $self); } $self->connection(@info); return $self; } - my $schema = $self->compose_namespace($target, $base); + my $schema = $self->compose_namespace($target, 'DBIx::Class::ResultSetProxy'); quote_sub "${target}::schema", '$s', { '$s' => \$schema }; $schema->connection(@info); @@ -1500,9 +1537,9 @@ sub compose_connection { 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; }