X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FResultSource.pm;h=1da92f4b781183507400477919f9c43a866f23cb;hb=c13002976e32b818eabc3a8eaf6fa2e23ebed7e9;hp=59db15719bc7ffbbe379a068c53e8fc7935d5aff;hpb=8bbfe6b24770ef48b722f3b018500ada5fed29cd;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/ResultSource.pm b/lib/DBIx/Class/ResultSource.pm index 59db157..1da92f4 100644 --- a/lib/DBIx/Class/ResultSource.pm +++ b/lib/DBIx/Class/ResultSource.pm @@ -5,6 +5,8 @@ use warnings; use DBIx::Class::ResultSet; use DBIx::Class::ResultSourceHandle; + +use DBIx::Class::Exception; use Carp::Clan qw/^DBIx::Class/; use base qw/DBIx::Class/; @@ -26,9 +28,8 @@ DBIx::Class::ResultSource - Result source object # Create a table based result source, in a result class. package MyDB::Schema::Result::Artist; - use base qw/DBIx::Class/; + use base qw/DBIx::Class::Core/; - __PACKAGE__->load_components(qw/Core/); __PACKAGE__->table('artist'); __PACKAGE__->add_columns(qw/ artistid name /); __PACKAGE__->set_primary_key('artistid'); @@ -38,8 +39,9 @@ DBIx::Class::ResultSource - Result source object # Create a query (view) based result source, in a result class package MyDB::Schema::Result::Year2000CDs; + use base qw/DBIx::Class::Core/; - __PACKAGE__->load_components('Core'); + __PACKAGE__->load_components('InflateColumn::DateTime'); __PACKAGE__->table_class('DBIx::Class::ResultSource::View'); __PACKAGE__->table('year2000cds'); @@ -58,10 +60,10 @@ sources, for example L. Table is the default result source type, so one is created for you when defining a result class as described in the synopsis above. -More specifically, the L component pulls in the -L as a base class, which -defines the L -method. When called, C creates and stores an instance of +More specifically, the L base class pulls in the +L component, which defines +the L method. +When called, C
creates and stores an instance of L. Luckily, to use tables as result sources, you don't need to remember any of this. @@ -387,7 +389,7 @@ sub columns { my $self = shift; $self->throw_exception( "columns() is a read-only accessor, did you mean add_columns()?" - ) if (@_ > 1); + ) if @_; return @{$self->{_ordered_columns}||[]}; } @@ -1186,15 +1188,9 @@ sub _compare_relationship_keys { return $found; } -sub resolve_join { - carp 'resolve_join is a private method, stop calling it'; - my $self = shift; - $self->_resolve_join (@_); -} - # Returns the {from} structure used to express JOIN conditions sub _resolve_join { - my ($self, $join, $alias, $seen, $jpath, $force_left) = @_; + my ($self, $join, $alias, $seen, $jpath, $parent_force_left) = @_; # we need a supplied one, because we do in-place modifications, no returns $self->throw_exception ('You must supply a seen hashref as the 3rd argument to _resolve_join') @@ -1203,50 +1199,68 @@ sub _resolve_join { $self->throw_exception ('You must supply a joinpath arrayref as the 4th argument to _resolve_join') unless ref $jpath eq 'ARRAY'; - $jpath = [@$jpath]; + $jpath = [@$jpath]; # copy - if (ref $join eq 'ARRAY') { + if (not defined $join) { + return (); + } + elsif (ref $join eq 'ARRAY') { return map { - $self->_resolve_join($_, $alias, $seen, $jpath, $force_left); + $self->_resolve_join($_, $alias, $seen, $jpath, $parent_force_left); } @$join; - } elsif (ref $join eq 'HASH') { - return - map { - my $as = ($seen->{$_} ? join ('_', $_, $seen->{$_} + 1) : $_); # the actual seen value will be incremented below - local $force_left->{force} = $force_left->{force}; - ( - $self->_resolve_join($_, $alias, $seen, [@$jpath], $force_left), - $self->related_source($_)->_resolve_join( - $join->{$_}, $as, $seen, [@$jpath, $_], $force_left - ) - ); - } keys %$join; - } elsif (ref $join) { - $self->throw_exception("No idea how to resolve join reftype ".ref $join); - } else { + } + elsif (ref $join eq 'HASH') { - return() unless defined $join; + my @ret; + for my $rel (keys %$join) { - my $count = ++$seen->{$join}; - my $as = ($count > 1 ? "${join}_${count}" : $join); + my $rel_info = $self->relationship_info($rel) + or $self->throw_exception("No such relationship ${rel}"); - my $rel_info = $self->relationship_info($join); - $self->throw_exception("No such relationship ${join}") unless $rel_info; - my $type; - if ($force_left) { - $type = 'left'; - } - else { - $type = $rel_info->{attrs}{join_type}; - $force_left = 1 if lc($type||'') eq 'left'; + my $force_left = $parent_force_left; + $force_left ||= lc($rel_info->{attrs}{join_type}||'') eq 'left'; + + # the actual seen value will be incremented by the recursion + my $as = $self->storage->relname_to_table_alias( + $rel, ($seen->{$rel} && $seen->{$rel} + 1) + ); + + push @ret, ( + $self->_resolve_join($rel, $alias, $seen, [@$jpath], $force_left), + $self->related_source($rel)->_resolve_join( + $join->{$rel}, $as, $seen, [@$jpath, { $rel => $as }], $force_left + ) + ); } + return @ret; + + } + elsif (ref $join) { + $self->throw_exception("No idea how to resolve join reftype ".ref $join); + } + else { + my $count = ++$seen->{$join}; + my $as = $self->storage->relname_to_table_alias( + $join, ($count > 1 && $count) + ); + + my $rel_info = $self->relationship_info($join) + or $self->throw_exception("No such relationship ${join}"); my $rel_src = $self->related_source($join); return [ { $as => $rel_src->from, -source_handle => $rel_src->handle, - -join_type => $type, - -join_path => [@$jpath, $join], + -join_type => $parent_force_left + ? 'left' + : $rel_info->{attrs}{join_type} + , + -join_path => [@$jpath, { $join => $as } ], + -is_single => ( + $rel_info->{attrs}{accessor} + && + List::Util::first { $rel_info->{attrs}{accessor} eq $_ } (qw/single filter/) + ), -alias => $as, -relation_chain_depth => $seen->{-relation_chain_depth} || 0, }, @@ -1323,13 +1337,13 @@ sub _resolve_condition { unless ($for->has_column_loaded($v)) { if ($for->in_storage) { $self->throw_exception(sprintf - 'Unable to resolve relationship from %s to %s: column %s.%s not ' - . 'loaded from storage (or not passed to new() prior to insert()). ' - . 'Maybe you forgot to call ->discard_changes to get defaults from the db.', - - $for->result_source->source_name, + "Unable to resolve relationship '%s' from object %s: column '%s' not " + . 'loaded from storage (or not passed to new() prior to insert()). You ' + . 'probably need to call ->discard_changes to get the server-side defaults ' + . 'from the database.', $as, - $as, $v, + $for, + $v, ); } return $UNRESOLVABLE_CONDITION; @@ -1357,89 +1371,20 @@ sub _resolve_condition { } } -# Legacy code, needs to go entirely away (fully replaced by _resolve_prefetch) -sub resolve_prefetch { - carp 'resolve_prefetch is a private method, stop calling it'; - - my ($self, $pre, $alias, $seen, $order, $collapse) = @_; - $seen ||= {}; - if( ref $pre eq 'ARRAY' ) { - return - map { $self->resolve_prefetch( $_, $alias, $seen, $order, $collapse ) } - @$pre; - } - elsif( ref $pre eq 'HASH' ) { - my @ret = - map { - $self->resolve_prefetch($_, $alias, $seen, $order, $collapse), - $self->related_source($_)->resolve_prefetch( - $pre->{$_}, "${alias}.$_", $seen, $order, $collapse) - } keys %$pre; - return @ret; - } - elsif( ref $pre ) { - $self->throw_exception( - "don't know how to resolve prefetch reftype ".ref($pre)); - } - else { - my $count = ++$seen->{$pre}; - my $as = ($count > 1 ? "${pre}_${count}" : $pre); - my $rel_info = $self->relationship_info( $pre ); - $self->throw_exception( $self->name . " has no such relationship '$pre'" ) - unless $rel_info; - my $as_prefix = ($alias =~ /^.*?\.(.+)$/ ? $1.'.' : ''); - my $rel_source = $self->related_source($pre); - - if (exists $rel_info->{attrs}{accessor} - && $rel_info->{attrs}{accessor} eq 'multi') { - $self->throw_exception( - "Can't prefetch has_many ${pre} (join cond too complex)") - unless ref($rel_info->{cond}) eq 'HASH'; - my $dots = @{[$as_prefix =~ m/\./g]} + 1; # +1 to match the ".${as_prefix}" - if (my ($fail) = grep { @{[$_ =~ m/\./g]} == $dots } - keys %{$collapse}) { - my ($last) = ($fail =~ /([^\.]+)$/); - carp ( - "Prefetching multiple has_many rels ${last} and ${pre} " - .(length($as_prefix) - ? "at the same level (${as_prefix}) " - : "at top level " - ) - . 'will explode the number of row objects retrievable via ->next or ->all. ' - . 'Use at your own risk.' - ); - } - #my @col = map { (/^self\.(.+)$/ ? ("${as_prefix}.$1") : ()); } - # values %{$rel_info->{cond}}; - $collapse->{".${as_prefix}${pre}"} = [ $rel_source->primary_columns ]; - # action at a distance. prepending the '.' allows simpler code - # in ResultSet->_collapse_result - my @key = map { (/^foreign\.(.+)$/ ? ($1) : ()); } - keys %{$rel_info->{cond}}; - my @ord = (ref($rel_info->{attrs}{order_by}) eq 'ARRAY' - ? @{$rel_info->{attrs}{order_by}} - : (defined $rel_info->{attrs}{order_by} - ? ($rel_info->{attrs}{order_by}) - : ())); - push(@$order, map { "${as}.$_" } (@key, @ord)); - } - - return map { [ "${as}.$_", "${as_prefix}${pre}.$_", ] } - $rel_source->columns; - } -} # Accepts one or more relationships for the current source and returns an # array of column names for each of those relationships. Column names are # prefixed relative to the current source, in accordance with where they appear -# in the supplied relationships. Needs an alias_map generated by -# $rs->_joinpath_aliases +# in the supplied relationships. sub _resolve_prefetch { my ($self, $pre, $alias, $alias_map, $order, $collapse, $pref_path) = @_; $pref_path ||= []; - if( ref $pre eq 'ARRAY' ) { + if (not defined $pre) { + return (); + } + elsif( ref $pre eq 'ARRAY' ) { return map { $self->_resolve_prefetch( $_, $alias, $alias_map, $order, $collapse, [ @$pref_path ] ) } @$pre; @@ -1462,7 +1407,7 @@ sub _resolve_prefetch { $p = $p->{$_} for (@$pref_path, $pre); $self->throw_exception ( - "Unable to resolve prefetch $pre - join alias map does not contain an entry for path: " + "Unable to resolve prefetch '$pre' - join alias map does not contain an entry for path: " . join (' -> ', @$pref_path, $pre) ) if (ref $p->{-join_aliases} ne 'ARRAY' or not @{$p->{-join_aliases}} ); @@ -1474,8 +1419,7 @@ sub _resolve_prefetch { my $as_prefix = ($alias =~ /^.*?\.(.+)$/ ? $1.'.' : ''); my $rel_source = $self->related_source($pre); - if (exists $rel_info->{attrs}{accessor} - && $rel_info->{attrs}{accessor} eq 'multi') { + if ($rel_info->{attrs}{accessor} && $rel_info->{attrs}{accessor} eq 'multi') { $self->throw_exception( "Can't prefetch has_many ${pre} (join cond too complex)") unless ref($rel_info->{cond}) eq 'HASH'; @@ -1502,7 +1446,8 @@ sub _resolve_prefetch { keys %{$rel_info->{cond}}; my @ord = (ref($rel_info->{attrs}{order_by}) eq 'ARRAY' ? @{$rel_info->{attrs}{order_by}} - : (defined $rel_info->{attrs}{order_by} + + : (defined $rel_info->{attrs}{order_by} ? ($rel_info->{attrs}{order_by}) : ())); push(@$order, map { "${as}.$_" } (@key, @ord)); @@ -1565,7 +1510,7 @@ L. =cut sub handle { - return new DBIx::Class::ResultSourceHandle({ + return DBIx::Class::ResultSourceHandle->new({ schema => $_[0]->schema, source_moniker => $_[0]->source_name }); @@ -1579,10 +1524,12 @@ See L. sub throw_exception { my $self = shift; + if (defined $self->schema) { $self->schema->throw_exception(@_); - } else { - croak(@_); + } + else { + DBIx::Class::Exception->throw(@_); } } @@ -1618,7 +1565,7 @@ Creates a new ResultSource object. Not normally called directly by end users. __PACKAGE__->column_info_from_storage(1); Enables the on-demand automatic loading of the above column -metadata from storage as neccesary. This is *deprecated*, and +metadata from storage as necessary. This is *deprecated*, and should not be used. It will be removed before 1.0.