X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FResultSet.pm;h=f9f27965395c60a06cdfd162d0ac3d1dbdbef27b;hb=c377d939ac84c511546add4679e2b7054a5abdab;hp=f72dc6663f2e0830dfa2bee236687eceb8805945;hpb=7223a5ce4537097282af3bc892cf6477baa6a3a7;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/ResultSet.pm b/lib/DBIx/Class/ResultSet.pm index f72dc66..f9f2796 100644 --- a/lib/DBIx/Class/ResultSet.pm +++ b/lib/DBIx/Class/ResultSet.pm @@ -162,25 +162,29 @@ sub search_rs { my $attrs = {}; $attrs = pop(@_) if @_ > 1 and ref $_[$#_] eq 'HASH'; - my $our_attrs = ($attrs->{_parent_attrs}) ? { %{$attrs->{_parent_attrs}} } : { %{$self->{attrs}} }; + my $our_attrs = ($attrs->{_parent_attrs}) + ? { %{$attrs->{_parent_attrs}} } + : { %{$self->{attrs}} }; + delete($attrs->{_parent_attrs}) if(exists $attrs->{_parent_attrs}); my $having = delete $our_attrs->{having}; - # XXX this is getting messy - if ($attrs->{_live_join_stack}) { - my $live_join = $attrs->{_live_join_stack}; - foreach (reverse @{$live_join}) { - $attrs->{_live_join_h} = (defined $attrs->{_live_join_h}) ? { $_ => $attrs->{_live_join_h} } : $_; - } - } + # XXX should only maintain _live_join_stack and generate _live_join_h from that + if ($attrs->{_live_join_stack}) { + my $live_join = $attrs->{_live_join_stack}; + foreach (reverse @{$live_join}) { + $attrs->{_live_join_h} = (defined $attrs->{_live_join_h}) ? { $_ => $attrs->{_live_join_h} } : $_; + } + } - # merge new attrs into old + # merge new attrs into inherited foreach my $key (qw/join prefetch/) { next unless (exists $attrs->{$key}); if ($attrs->{_live_join_stack} || $our_attrs->{_live_join_stack}) { - my $live_join = $attrs->{_live_join_stack} || $our_attrs->{_live_join_stack}; - foreach (reverse @{$live_join}) { - $attrs->{$key} = { $_ => $attrs->{$key} }; - } + my $live_join = $attrs->{_live_join_stack} || + $our_attrs->{_live_join_stack}; + foreach (reverse @{$live_join}) { + $attrs->{$key} = { $_ => $attrs->{$key} }; + } } if (exists $our_attrs->{$key}) { @@ -191,47 +195,63 @@ sub search_rs { delete $attrs->{$key}; } - $our_attrs->{join} = $self->_merge_attr($our_attrs->{join}, $attrs->{_live_join_h}, 1) if ($attrs->{_live_join_h}); + $our_attrs->{join} = $self->_merge_attr( + $our_attrs->{join}, $attrs->{_live_join_h} + ) if ($attrs->{_live_join_h}); - if (exists $our_attrs->{prefetch}) { - $our_attrs->{join} = $self->_merge_attr($our_attrs->{join}, $our_attrs->{prefetch}, 1); + if (defined $our_attrs->{prefetch}) { + $our_attrs->{join} = $self->_merge_attr( + $our_attrs->{join}, $our_attrs->{prefetch} + ); } my $new_attrs = { %{$our_attrs}, %{$attrs} }; my $where = (@_ - ? ((@_ == 1 || ref $_[0] eq "HASH") - ? shift - : ((@_ % 2) - ? $self->throw_exception( - "Odd number of arguments to search") - : {@_})) - : undef()); + ? ( + (@_ == 1 || ref $_[0] eq "HASH") + ? shift + : ( + (@_ % 2) + ? $self->throw_exception("Odd number of arguments to search") + : {@_} + ) + ) + : undef() + ); + if (defined $where) { - $new_attrs->{where} = (defined $new_attrs->{where} - ? { '-and' => - [ map { ref $_ eq 'ARRAY' ? [ -or => $_ ] : $_ } - $where, $new_attrs->{where} ] } - : $where); + $new_attrs->{where} = ( + defined $new_attrs->{where} + ? { '-and' => [ + map { + ref $_ eq 'ARRAY' ? [ -or => $_ ] : $_ + } $where, $new_attrs->{where} + ] + } + : $where); } if (defined $having) { - $new_attrs->{having} = (defined $new_attrs->{having} - ? { '-and' => - [ map { ref $_ eq 'ARRAY' ? [ -or => $_ ] : $_ } - $having, $new_attrs->{having} ] } - : $having); + $new_attrs->{having} = ( + defined $new_attrs->{having} + ? { '-and' => [ + map { + ref $_ eq 'ARRAY' ? [ -or => $_ ] : $_ + } $having, $new_attrs->{having} + ] + } + : $having); } my $rs = (ref $self)->new($self->result_source, $new_attrs); - $rs->{_parent_rs} = $self->{_parent_rs} if ($self->{_parent_rs}); #XXX - hack to pass through parent of related resultsets + $rs->{_parent_rs} = $self->{_parent_rs} if ($self->{_parent_rs}); - unless (@_) { # no search, effectively just a clone + unless (@_) { # no search, effectively just a clone my $rows = $self->get_cache; if ($rows) { $rs->set_cache($rows); } } - return $rs; } @@ -278,7 +298,9 @@ a row by its primary key: You can also find a row by a specific unique constraint using the C attribute. For example: - my $cd = $schema->resultset('CD')->find('Massive Attack', 'Mezzanine', { key => 'cd_artist_title' }); + my $cd = $schema->resultset('CD')->find('Massive Attack', 'Mezzanine', { + key => 'cd_artist_title' + }); Additionally, you can specify the columns explicitly by name: @@ -368,7 +390,9 @@ sub _unique_queries { # Add the ResultSet's alias foreach my $key (grep { ! m/\./ } keys %$unique_query) { - my $alias = ($self->{attrs}->{_live_join}) ? $self->{attrs}->{_live_join} : $self->{attrs}->{alias}; + my $alias = ($self->{attrs}->{_live_join}) + ? $self->{attrs}->{_live_join} + : $self->{attrs}->{alias}; $unique_query->{"$alias.$key"} = delete $unique_query->{$key}; } @@ -397,7 +421,7 @@ sub _build_unique_query { =over 4 -=item Arguments: $cond, \%attrs? +=item Arguments: $rel, $cond, \%attrs? =item Return Value: $new_resultset @@ -484,8 +508,10 @@ sub single { } my @data = $self->result_source->storage->select_single( - $attrs->{from}, $attrs->{select}, - $attrs->{where},$attrs); + $attrs->{from}, $attrs->{select}, + $attrs->{where},$attrs + ); + return (@data ? $self->_construct_object(@data) : ()); } @@ -498,11 +524,14 @@ sub _is_unique_query { my ($self, $query) = @_; my $collapsed = $self->_collapse_query($query); + my $alias = ($self->{attrs}->{_live_join}) + ? $self->{attrs}->{_live_join} + : $self->{attrs}->{alias}; - my $alias = ($self->{attrs}->{_live_join}) ? $self->{attrs}->{_live_join} : $self->{attrs}->{alias}; foreach my $name ($self->result_source->unique_constraint_names) { - my @unique_cols = map { "$alias.$_" } - $self->result_source->unique_constraint_columns($name); + my @unique_cols = map { + "$alias.$_" + } $self->result_source->unique_constraint_columns($name); # Count the values for each unique column my %seen = map { $_ => 0 } @unique_cols; @@ -574,7 +603,6 @@ Returns a ResultSetColumn instance for $column based on $self sub get_column { my ($self, $column) = @_; - my $new = DBIx::Class::ResultSetColumn->new($self, $column); return $new; } @@ -672,9 +700,10 @@ sub next { $self->{all_cache_position} = 1; return ($self->all)[0]; } - my @row = (exists $self->{stashed_row} ? - @{delete $self->{stashed_row}} : - $self->cursor->next + my @row = ( + exists $self->{stashed_row} + ? @{delete $self->{stashed_row}} + : $self->cursor->next ); return unless (@row); return $self->_construct_object(@row); @@ -686,10 +715,13 @@ sub _resolve { return if(exists $self->{_attrs}); #return if _resolve has already been called my $attrs = $self->{attrs}; - my $source = ($self->{_parent_rs}) ? $self->{_parent_rs} : $self->{result_source}; + my $source = ($self->{_parent_rs}) + ? $self->{_parent_rs} + : $self->{result_source}; # XXX - lose storable dclone - my $record_filter = delete $attrs->{record_filter} if (defined $attrs->{record_filter}); + my $record_filter = delete $attrs->{record_filter} + if (defined $attrs->{record_filter}); $attrs = Storable::dclone($attrs || {}); # { %{ $attrs || {} } }; $attrs->{record_filter} = $record_filter if ($record_filter); $self->{attrs}->{record_filter} = $record_filter if ($record_filter); @@ -698,17 +730,20 @@ sub _resolve { $attrs->{columns} ||= delete $attrs->{cols} if $attrs->{cols}; delete $attrs->{as} if $attrs->{columns}; - $attrs->{columns} ||= [ $self->{result_source}->columns ] unless $attrs->{select}; - my $select_alias = ($self->{_parent_rs}) ? $self->{attrs}->{_live_join} : $alias; + $attrs->{columns} ||= [ $self->{result_source}->columns ] + unless $attrs->{select}; + my $select_alias = ($self->{_parent_rs}) + ? $self->{attrs}->{_live_join} + : $alias; $attrs->{select} = [ - map { m/\./ ? $_ : "${select_alias}.$_" } @{delete $attrs->{columns}} - ] if $attrs->{columns}; + map { m/\./ ? $_ : "${select_alias}.$_" } @{delete $attrs->{columns}} + ] if $attrs->{columns}; $attrs->{as} ||= [ - map { m/^\Q$alias.\E(.+)$/ ? $1 : $_ } @{$attrs->{select}} - ]; + map { m/^\Q$alias.\E(.+)$/ ? $1 : $_ } @{$attrs->{select}} + ]; if (my $include = delete $attrs->{include_columns}) { - push(@{$attrs->{select}}, @$include); - push(@{$attrs->{as}}, map { m/([^.]+)$/; $1; } @$include); + push(@{$attrs->{select}}, @$include); + push(@{$attrs->{as}}, map { m/([^.]+)$/; $1; } @$include); } $attrs->{from} ||= [ { $alias => $source->from } ]; @@ -722,8 +757,9 @@ sub _resolve { $seen{$j} = 1; } } - - push(@{$attrs->{from}}, $source->resolve_join($join, $attrs->{alias}, $attrs->{seen_join})); + push(@{$attrs->{from}}, + $source->resolve_join($join, $attrs->{alias}, $attrs->{seen_join}) + ); } $attrs->{group_by} ||= $attrs->{select} if delete $attrs->{distinct}; $attrs->{order_by} = [ $attrs->{order_by} ] if @@ -733,9 +769,9 @@ sub _resolve { if(my $seladds = delete($attrs->{'+select'})) { my @seladds = (ref($seladds) eq 'ARRAY' ? @$seladds : ($seladds)); $attrs->{select} = [ - @{ $attrs->{select} }, - map { (m/\./ || ref($_)) ? $_ : "${alias}.$_" } $seladds - ]; + @{ $attrs->{select} }, + map { (m/\./ || ref($_)) ? $_ : "${alias}.$_" } $seladds + ]; } if(my $asadds = delete($attrs->{'+as'})) { my @asadds = (ref($asadds) eq 'ARRAY' ? @$asadds : ($asadds)); @@ -743,72 +779,41 @@ sub _resolve { } my $collapse = $attrs->{collapse} || {}; if (my $prefetch = delete $attrs->{prefetch}) { - my @pre_order; - foreach my $p (ref $prefetch eq 'ARRAY' ? @$prefetch : ($prefetch)) { - if ( ref $p eq 'HASH' ) { - foreach my $key (keys %$p) { - push(@{$attrs->{from}}, $source->resolve_join($p, $attrs->{alias})) - unless $seen{$key}; - } - } else { - push(@{$attrs->{from}}, $source->resolve_join($p, $attrs->{alias})) - unless $seen{$p}; - } - - # we're about to resolve_join on the current class, so we need to bring - # the joins (which are from the original class) to the right level - # XXX the below alg is ridiculous - if ($attrs->{_live_join_stack}) { - STACK: foreach (@{$attrs->{_live_join_stack}}) { - if (ref $p eq 'HASH') { - if (exists $p->{$_}) { - $p = $p->{$_}; - } else { - $p = undef; - last STACK; - } - } elsif (ref $p eq 'ARRAY') { - foreach my $pe (@{$p}) { - if ($pe eq $_) { - $p = undef; - last STACK; - } - next unless(ref $pe eq 'HASH'); - next unless(exists $pe->{$_}); - $p = $pe->{$_}; - next STACK; - } - $p = undef; - last STACK; - } else { - $p = undef; - last STACK; - } - } - } - - if ($p) { - my @prefetch = $self->result_source->resolve_prefetch( - $p, $attrs->{alias}, {}, \@pre_order, $collapse); - - push(@{$attrs->{select}}, map { $_->[0] } @prefetch); - push(@{$attrs->{as}}, map { $_->[1] } @prefetch); - } + my @pre_order; + foreach my $p (ref $prefetch eq 'ARRAY' ? @$prefetch : ($prefetch)) { + if ( ref $p eq 'HASH' ) { + foreach my $key (keys %$p) { + push(@{$attrs->{from}}, $source->resolve_join($p, $attrs->{alias})) + unless $seen{$key}; + } + } else { + push(@{$attrs->{from}}, $source->resolve_join($p, $attrs->{alias})) + unless $seen{$p}; + } + # bring joins back to level of current class + $p = $self->_reduce_joins($p, $attrs) if ($attrs->{_live_join_stack}); + if ($p) { + my @prefetch = $self->result_source->resolve_prefetch( + $p, $attrs->{alias}, {}, \@pre_order, $collapse + ); + push(@{$attrs->{select}}, map { $_->[0] } @prefetch); + push(@{$attrs->{as}}, map { $_->[1] } @prefetch); } - push(@{$attrs->{order_by}}, @pre_order); + } + push(@{$attrs->{order_by}}, @pre_order); } $attrs->{collapse} = $collapse; $self->{_attrs} = $attrs; } sub _merge_attr { - my ($self, $a, $b, $is_prefetch) = @_; + my ($self, $a, $b) = @_; return $b unless $a; if (ref $b eq 'HASH' && ref $a eq 'HASH') { foreach my $key (keys %{$b}) { if (exists $a->{$key}) { - $a->{$key} = $self->_merge_attr($a->{$key}, $b->{$key}, $is_prefetch); + $a->{$key} = $self->_merge_attr($a->{$key}, $b->{$key}); } else { $a->{$key} = $b->{$key}; } @@ -823,12 +828,12 @@ sub _merge_attr { foreach ($a, $b) { foreach my $element (@{$_}) { if (ref $element eq 'HASH') { - $hash = $self->_merge_attr($hash, $element, $is_prefetch); + $hash = $self->_merge_attr($hash, $element); } elsif (ref $element eq 'ARRAY') { $array = [@{$array}, @{$element}]; - } else { - if (($b == $_) && $is_prefetch) { - $self->_merge_array($array, $element, $is_prefetch); + } else { + if ($b == $_) { + $self->_merge_array($array, $element); } else { push(@{$array}, $element); } @@ -836,15 +841,15 @@ sub _merge_attr { } } - my $final_array = []; - foreach my $element (@{$array}) { - push(@{$final_array}, $element) unless (exists $hash->{$element}); - } - $array = $final_array; + my $final_array = []; + foreach my $element (@{$array}) { + push(@{$final_array}, $element) unless (exists $hash->{$element}); + } + $array = $final_array; if ((keys %{$hash}) && (scalar(@{$array} > 0))) { return [$hash, @{$array}]; - } else { + } else { return (keys %{$hash}) ? $hash : $array; } } @@ -860,6 +865,37 @@ sub _merge_array { } } +# bring the joins (which are from the original class) to the level +# of the current class so that we can resolve them properly +sub _reduce_joins { + my ($self, $p, $attrs) = @_; + + STACK: + foreach (@{$attrs->{_live_join_stack}}) { + if (ref $p eq 'HASH') { + if (exists $p->{$_}) { + $p = $p->{$_}; + } else { + return undef; + } + } elsif (ref $p eq 'ARRAY') { + foreach my $pe (@{$p}) { + if ($pe eq $_) { + return undef; + } + if ((ref $pe eq 'HASH') && (exists $pe->{$_})) { + $p = $pe->{$_}; + next STACK; + } + } + return undef; + } else { + return undef; + } + } + return $p; +} + sub _construct_object { my ($self, @row) = @_; my @as = @{ $self->{_attrs}{as} }; @@ -927,10 +963,13 @@ sub _collapse_result { my %co_check = map { ($_, $tree->[0]->{$_}); } @co_key; my (@final, @raw); - while ( !(grep { - !defined($tree->[0]->{$_}) || - $co_check{$_} ne $tree->[0]->{$_} - } @co_key) ) { + while ( + !( + grep { + !defined($tree->[0]->{$_}) || $co_check{$_} ne $tree->[0]->{$_} + } @co_key + ) + ) { push(@final, $tree); last unless (@raw = $self->cursor->next); $row = $self->{stashed_row} = \@raw; @@ -1023,8 +1062,9 @@ sub _count { # Separated out so pager can get the full count # offset, order by and page are not needed to count. record_filter is cdbi delete $attrs->{$_} for qw/rows offset order_by page pager record_filter/; - my $tmp_rs = (ref $self)->new($self->result_source, $attrs); - $tmp_rs->{_parent_rs} = $self->{_parent_rs} if ($self->{_parent_rs}); #XXX - hack to pass through parent of related resultsets + my $tmp_rs = (ref $self)->new($self->result_source, $attrs); + $tmp_rs->{_parent_rs} = $self->{_parent_rs} if ($self->{_parent_rs}); + #XXX - hack to pass through parent of related resultsets my ($count) = $tmp_rs->cursor->next; return $count; @@ -1609,28 +1649,33 @@ sub related_resultset { return $self->{related_resultsets}{$rel} ||= do { #warn "fetching related resultset for rel '$rel' " . $self->result_source->{name}; my $rel_obj = $self->result_source->relationship_info($rel); - #print Dumper($self->result_source->_relationships); + #print Dumper($self->result_source->_relationships); $self->throw_exception( - "search_related: result source '" . $self->result_source->name . + "search_related: result source '" . $self->result_source->name . "' has no such relationship ${rel}") - unless $rel_obj; #die Dumper $self->{attrs}; - - my @live_join_stack = (exists $self->{attrs}->{_live_join_stack}) ? - @{$self->{attrs}->{_live_join_stack}}: - (); - push(@live_join_stack, $rel); - - my $rs = $self->result_source->schema->resultset($rel_obj->{class} - )->search( undef, - { select => undef, - as => undef, - _live_join => $rel, #the most recent - _live_join_stack => \@live_join_stack, #the trail of rels - _parent_attrs => $self->{attrs}} - ); + unless $rel_obj; #die Dumper $self->{attrs}; + + my @live_join_stack = ( + exists $self->{attrs}->{_live_join_stack}) + ? @{$self->{attrs}->{_live_join_stack}} + : (); + + push(@live_join_stack, $rel); + + my $rs = $self->result_source->schema->resultset($rel_obj->{class})->search( + undef, { + select => undef, + as => undef, + _live_join => $rel, #the most recent + _live_join_stack => \@live_join_stack, #the trail of rels + _parent_attrs => $self->{attrs}} + ); # keep reference of the original resultset - $rs->{_parent_rs} = ($self->{_parent_rs}) ? $self->{_parent_rs} : $self->result_source; + $rs->{_parent_rs} = ($self->{_parent_rs}) + ? $self->{_parent_rs} + : $self->result_source; + return $rs; }; } @@ -1948,6 +1993,20 @@ done. Set to 1 to group by all columns. +=head2 where + +=over 4 + +Adds to the WHERE clause. + + # only return rows WHERE deleted IS NULL for all searches + __PACKAGE__->resultset_attributes({ where => { deleted => undef } }); ) + +Can be overridden by passing C<{ where => undef }> as an attribute +to a resulset. + +=back + =head2 cache Set to 1 to cache search results. This prevents extra SQL queries if you