X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FResultSet.pm;h=146c12bbbba8b4fc4c66e6a621f5c0c926ed11e0;hb=3334d204fcbbddedd73a7f63a285bdda9cb3e031;hp=df7b7012731a7e7d3feadb10b60bd464f29e8fcc;hpb=1e1cea3877c13cf9dcccefe2ba7ac9f089e2d4bc;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/ResultSet.pm b/lib/DBIx/Class/ResultSet.pm index df7b701..146c12b 100644 --- a/lib/DBIx/Class/ResultSet.pm +++ b/lib/DBIx/Class/ResultSet.pm @@ -6,6 +6,7 @@ use base qw/DBIx::Class/; use DBIx::Class::Carp; use DBIx::Class::ResultSetColumn; use Scalar::Util qw/blessed weaken reftype/; +use DBIx::Class::_Util 'fail_on_internal_wantarray'; use Try::Tiny; use Data::Compare (); # no imports!!! guard against insane architecture @@ -141,8 +142,8 @@ another. =head3 Resolving conditions and attributes -When a resultset is chained from another resultset (ie: -Csearch(\%extra_cond, \%attrs)>), conditions +When a resultset is chained from another resultset (e.g.: +C<< my $new_rs = $old_rs->search(\%extra_cond, \%attrs) >>), conditions and attributes with the same keys need resolving. If any of L, L, L are present, they reset the @@ -304,8 +305,8 @@ call it as C. For a list of attributes that can be passed to C, see L. For more examples of using this function, see -L. For a complete -documentation for the first argument, see L +L. For a complete +documentation for the first argument, see L and its extension L. For more help on using joins with search, see L. @@ -327,6 +328,7 @@ sub search { my $rs = $self->search_rs( @_ ); if (wantarray) { + DBIx::Class::_ENV_::ASSERT_NO_INTERNAL_WANTARRAY and my $sog = fail_on_internal_wantarray($rs); return $rs->all; } elsif (defined wantarray) { @@ -646,7 +648,7 @@ should only be used in that context. C is a convenience method. It is equivalent to calling C<< $schema->search(\[]) >>, but if you want to ensure columns are bound correctly, use L. -See L and +See L and L for searching techniques that do not require C. @@ -1082,7 +1084,7 @@ sub single { $attrs->{from}, $attrs->{select}, $attrs->{where}, $attrs )]; - $self->{_attrs}{_sqlmaker_select_args} = $attrs->{_sqlmaker_select_args}; + return undef unless @$data; $self->{_stashed_rows} = [ $data ]; $self->_construct_results->[0]; @@ -1211,8 +1213,6 @@ sub slice { $attrs->{offset} += $min; $attrs->{rows} = ($max ? ($max - $min + 1) : 1); return $self->search(undef, $attrs); - #my $slice = (ref $self)->new($self->result_source, $attrs); - #return (wantarray ? $slice->all : $slice); } =head2 next @@ -1299,21 +1299,24 @@ sub _construct_results { $attrs->{_order_is_artificial} = 1; } - my $cursor = $self->cursor; - # this will be used as both initial raw-row collector AND as a RV of # _construct_results. Not regrowing the array twice matters a lot... # a surprising amount actually my $rows = delete $self->{_stashed_rows}; + my $cursor; # we may not need one at all + my $did_fetch_all = $fetch_all; if ($fetch_all) { # FIXME SUBOPTIMAL - we can do better, cursor->next/all (well diff. methods) should return a ref - $rows = [ ($rows ? @$rows : ()), $cursor->all ]; + $rows = [ ($rows ? @$rows : ()), $self->cursor->all ]; } elsif( $attrs->{collapse} ) { + # a cursor will need to be closed over in case of collapse + $cursor = $self->cursor; + $attrs->{_ordered_for_collapse} = ( ( $attrs->{order_by} @@ -1339,6 +1342,7 @@ sub _construct_results { if (! $did_fetch_all and ! @{$rows||[]} ) { # FIXME SUBOPTIMAL - we can do better, cursor->next/all (well diff. methods) should return a ref + $cursor ||= $self->cursor; if (scalar (my @r = $cursor->next) ) { $rows = [ \@r ]; } @@ -1376,16 +1380,6 @@ sub _construct_results { } } - my @extra_collapser_args; - if ($attrs->{collapse} and ! $did_fetch_all ) { - - @extra_collapser_args = ( - # FIXME SUBOPTIMAL - we can do better, cursor->next/all (well diff. methods) should return a ref - sub { my @r = $cursor->next or return; \@r }, # how the collapser gets more rows - ($self->{_stashed_rows} = []), # where does it stuff excess - ); - } - # hotspot - skip the setter my $res_class = $self->_result_class; @@ -1439,35 +1433,79 @@ sub _construct_results { ); } } - # Special-case multi-object HRI (we always prune, and there is no $inflator_cref pass) - elsif ($self->{_result_inflator}{is_hri}) { + else { + my $parser_type = + $self->{_result_inflator}{is_hri} ? 'hri' + : $self->{_result_inflator}{is_core_row} ? 'classic_pruning' + : 'classic_nonpruning' + ; - # $args and $attrs to _mk_row_parser are seperated to delineate what is + # $args and $attrs to _mk_row_parser are separated to delineate what is # core collapser stuff and what is dbic $rs specific - ( $self->{_row_parser}{hri} ||= $rsrc->_mk_row_parser({ + @{$self->{_row_parser}{$parser_type}}{qw(cref nullcheck)} = $rsrc->_mk_row_parser({ eval => 1, inflate_map => $infmap, collapse => $attrs->{collapse}, premultiplied => $attrs->{_main_source_premultiplied}, - hri_style => 1, - prune_null_branches => 1, - }, $attrs) )->($rows, @extra_collapser_args); + hri_style => $self->{_result_inflator}{is_hri}, + prune_null_branches => $self->{_result_inflator}{is_hri} || $self->{_result_inflator}{is_core_row}, + }, $attrs) unless $self->{_row_parser}{$parser_type}{cref}; + + # column_info metadata historically hasn't been too reliable. + # We need to start fixing this somehow (the collapse resolver + # can't work without it). Add an explicit check for the *main* + # result, hopefully this will gradually weed out such errors + # + # FIXME - this is a temporary kludge that reduces performance + # It is however necessary for the time being + my ($unrolled_non_null_cols_to_check, $err); + + if (my $check_non_null_cols = $self->{_row_parser}{$parser_type}{nullcheck} ) { + + $err = + 'Collapse aborted due to invalid ResultSource metadata - the following ' + . 'selections are declared non-nullable but NULLs were retrieved: ' + ; + + my @violating_idx; + COL: for my $i (@$check_non_null_cols) { + ! defined $_->[$i] and push @violating_idx, $i and next COL for @$rows; + } + + $self->throw_exception( $err . join (', ', map { "'$infmap->[$_]'" } @violating_idx ) ) + if @violating_idx; + + $unrolled_non_null_cols_to_check = join (',', @$check_non_null_cols); + } + + my $next_cref = + ($did_fetch_all or ! $attrs->{collapse}) ? undef + : defined $unrolled_non_null_cols_to_check ? eval sprintf <<'EOS', $unrolled_non_null_cols_to_check +sub { + # FIXME SUBOPTIMAL - we can do better, cursor->next/all (well diff. methods) should return a ref + my @r = $cursor->next or return; + if (my @violating_idx = grep { ! defined $r[$_] } (%s) ) { + $self->throw_exception( $err . join (', ', map { "'$infmap->[$_]'" } @violating_idx ) ) } - # Regular multi-object - else { - my $parser_type = $self->{_result_inflator}{is_core_row} ? 'classic_pruning' : 'classic_nonpruning'; + \@r +} +EOS + : sub { + # FIXME SUBOPTIMAL - we can do better, cursor->next/all (well diff. methods) should return a ref + my @r = $cursor->next or return; + \@r + } + ; - # $args and $attrs to _mk_row_parser are seperated to delineate what is - # core collapser stuff and what is dbic $rs specific - ( $self->{_row_parser}{$parser_type} ||= $rsrc->_mk_row_parser({ - eval => 1, - inflate_map => $infmap, - collapse => $attrs->{collapse}, - premultiplied => $attrs->{_main_source_premultiplied}, - prune_null_branches => $self->{_result_inflator}{is_core_row}, - }, $attrs) )->($rows, @extra_collapser_args); + $self->{_row_parser}{$parser_type}{cref}->( + $rows, + $next_cref ? ( $next_cref, $self->{_stashed_rows} = [] ) : (), + ); - $_ = $inflator_cref->($res_class, $rsrc, @$_) for @$rows; + # Special-case multi-object HRI - there is no $inflator_cref pass + unless ($self->{_result_inflator}{is_hri}) { + $_ = $inflator_cref->($res_class, $rsrc, @$_) for @$rows + } } # The @$rows check seems odd at first - why wouldn't we want to warn @@ -2151,7 +2189,7 @@ first element should be a list of column names and each subsequent element should be a data value in the earlier specified column order. For example: - $Arstist_rs->populate([ + $schema->resultset("Artist")->populate([ [ qw( artistid name ) ], [ 100, 'A Formally Unknown Singer' ], [ 101, 'A singer that jumped the shark two albums ago' ], @@ -2285,7 +2323,7 @@ sub populate { } -# populate() argumnets went over several incarnations +# populate() arguments went over several incarnations # What we ultimately support is AoH sub _normalize_populate_args { my ($self, $arg) = @_; @@ -2459,7 +2497,7 @@ sub _merge_with_rscond { ); } else { - # precendence must be given to passed values over values inherited from + # precedence must be given to passed values over values inherited from # the cond, so the order here is important. my $collapsed_cond = $self->_collapse_cond($self->{cond}); my %implied = %{$self->_remove_alias($collapsed_cond, $alias)}; @@ -2494,7 +2532,7 @@ sub _merge_with_rscond { # determines if the resultset defines at least one # of the attributes supplied # -# used to determine if a subquery is neccessary +# used to determine if a subquery is necessary # # supports some virtual attributes: # -join @@ -2641,7 +2679,7 @@ sub as_query { { artist => 'fred' }, { key => 'artists' }); $cd->cd_to_producer->find_or_new({ producer => $producer }, - { key => 'primary }); + { key => 'primary' }); Find an existing record from this resultset using L. if none exists, instantiate a new result object and return it. The object will not be saved @@ -3125,15 +3163,6 @@ sub related_resultset { #XXX - temp fix for result_class bug. There likely is a more elegant fix -groditi delete @{$attrs}{qw(result_class alias)}; - my $related_cache; - - if (my $cache = $self->get_cache) { - $related_cache = [ map - { @{$_->related_resultset($rel)->get_cache||[]} } - @$cache - ]; - } - my $rel_source = $rsrc->related_source($rel); my $new = do { @@ -3154,7 +3183,16 @@ sub related_resultset { where => $attrs->{where}, }); }; - $new->set_cache($related_cache) if $related_cache; + + if (my $cache = $self->get_cache) { + my @related_cache = map + { @{$_->related_resultset($rel)->get_cache||[]} } + @$cache + ; + + $new->set_cache(\@related_cache) if @related_cache; + } + $new; }; } @@ -3555,7 +3593,7 @@ sub _resolved_attrs { } # run through the resulting joinstructure (starting from our current slot) - # and unset collapse if proven unnesessary + # and unset collapse if proven unnecessary # # also while we are at it find out if the current root source has # been premultiplied by previous related_source chaining @@ -4181,7 +4219,7 @@ object with all of its related data. If an L is already declared, and orders the resultset in a way that makes collapsing as described above impossible (e.g. C<< ORDER BY has_many_rel.column >> or C), DBIC will automatically -switch to "eager" mode and slurp the entire resultset before consturcting the +switch to "eager" mode and slurp the entire resultset before constructing the first object returned by L. Setting this attribute on a resultset that does not join any has_many @@ -4605,7 +4643,7 @@ or to a sensible value based on the "data_type". =item dbic_colname Used to fill in missing sqlt_datatype and sqlt_size attributes (if they are -explicitly specified they are never overriden). Also used by some weird DBDs, +explicitly specified they are never overridden). Also used by some weird DBDs, where the column name should be available at bind_param time (e.g. Oracle). =back @@ -4616,6 +4654,7 @@ supported: [ $name => $val ] === [ { dbic_colname => $name }, $val ] [ \$dt => $val ] === [ { sqlt_datatype => $dt }, $val ] [ undef, $val ] === [ {}, $val ] + $val === [ {}, $val ] =head1 AUTHOR AND CONTRIBUTORS