X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FResultSet.pm;h=7e59438a5026bdbe86410c2e5a978aaf6b4e01bf;hb=6bf3e3b3fa88b45f33ff21ae38239c69bd14335c;hp=19a71aa3a9ab60f59c7ca4d42bd8362e139cca54;hpb=a258ee5dacc4e7e3c748f59e8335a7688f5455d0;p=dbsrgits%2FDBIx-Class-Historic.git diff --git a/lib/DBIx/Class/ResultSet.pm b/lib/DBIx/Class/ResultSet.pm index 19a71aa..7e59438 100644 --- a/lib/DBIx/Class/ResultSet.pm +++ b/lib/DBIx/Class/ResultSet.pm @@ -46,27 +46,13 @@ A new ResultSet is returned from calling L on an existing ResultSet. The new one will contain all the conditions of the original, plus any new conditions added in the C call. -A ResultSet is also an iterator. L is used to return all the -Ls the ResultSet represents. +A ResultSet also incorporates an implicit iterator. L and L +can be used to walk through all the Ls the ResultSet +represents. The query that the ResultSet represents is B executed against the database when these methods are called: - -=over - -=item L - -=item L - -=item L - -=item L - -=item L - -=item L - -=back +L L L L L L =head1 EXAMPLES @@ -674,7 +660,7 @@ L for more information. sub cursor { my ($self) = @_; - my $attrs = { %{$self->_resolved_attrs} }; + my $attrs = $self->_resolved_attrs_copy; return $self->{cursor} ||= $self->result_source->storage->select($attrs->{from}, $attrs->{select}, $attrs->{where},$attrs); @@ -725,7 +711,7 @@ sub single { $self->throw_exception('single() only takes search conditions, no attributes. You want ->search( $cond, $attrs )->single()'); } - my $attrs = { %{$self->_resolved_attrs} }; + my $attrs = $self->_resolved_attrs_copy; if ($where) { if (defined $attrs->{where}) { $attrs->{where} = { @@ -1145,8 +1131,8 @@ sub result_class { =back Performs an SQL C with the same query as the resultset was built -with to find the number of elements. If passed arguments, does a search -on the resultset and counts the results of that. +with to find the number of elements. Passing arguments is equivalent to +C<< $rs->search ($cond, \%attrs)->count >> =cut @@ -1155,10 +1141,11 @@ sub count { return $self->search(@_)->count if @_ and defined $_[0]; return scalar @{ $self->get_cache } if $self->get_cache; - my @subq_attrs = qw/prefetch collapse group_by having/; + my @subq_attrs = qw/prefetch collapse distinct group_by having/; + my $attrs = $self->_resolved_attrs; # if we are not paged - we are simply asking for a limit - if (not $self->{attrs}{page} and not $self->{attrs}{software_limit}) { + if (not $attrs->{page} and not $attrs->{software_limit}) { push @subq_attrs, qw/rows offset/; } @@ -1170,13 +1157,13 @@ sub count { sub _count_subq { my $self = shift; - my $attrs = { %{$self->_resolved_attrs} }; + my $attrs = $self->_resolved_attrs_copy; # copy for the subquery, we need to do some adjustments to it too my $sub_attrs = { %$attrs }; - # these can not go in the subquery either - delete $sub_attrs->{$_} for qw/prefetch select +select as +as columns +columns/; + # these can not go in the subquery, and there is no point of ordering it + delete $sub_attrs->{$_} for qw/prefetch collapse select +select as +as columns +columns order_by/; # force a group_by and the same set of columns (most databases require this) $sub_attrs->{columns} = $sub_attrs->{group_by} ||= [ map { "$attrs->{alias}.$_" } ($self->result_source->primary_columns) ]; @@ -1186,7 +1173,7 @@ sub _count_subq { }]; # the subquery replaces this - delete $attrs->{$_} for qw/where bind prefetch collapse group_by having/; + delete $attrs->{$_} for qw/where bind prefetch collapse distinct group_by having having_bind/; return $self->__count ($attrs); } @@ -1199,9 +1186,10 @@ sub _count_simple { # need to take offset from resolved attrs - $count -= $self->{_attrs}{offset} if $self->{_attrs}{offset}; - $count = $self->{attrs}{rows} if - $self->{attrs}{rows} and $self->{attrs}{rows} < $count; + my $attrs = $self->_resolved_attrs; + + $count -= $attrs->{offset} if $attrs->{offset}; + $count = $attrs->{rows} if $attrs->{rows} and $attrs->{rows} < $count; $count = 0 if ($count < 0); return $count; } @@ -1209,7 +1197,7 @@ sub _count_simple { sub __count { my ($self, $attrs) = @_; - $attrs ||= { %{$self->{attrs}} }; + $attrs ||= $self->_resolved_attrs_copy; # take off any column specs, any pagers, record_filter is cdbi, and no point of ordering a count delete $attrs->{$_} for (qw/columns +columns select +select as +as rows offset page pager order_by record_filter/); @@ -1333,14 +1321,68 @@ sub first { } -# _update_delete_via_subq -# -# Presence of some rs attributes requires a subquery to reliably -# update/deletre +# _rs_update_delete # +# Determines whether and what type of subquery is required for the $rs operation. +# If grouping is necessary either supplies its own, or verifies the current one +# After all is done delegates to the proper storage method. + +sub _rs_update_delete { + my ($self, $op, $values) = @_; + + my $rsrc = $self->result_source; + + my $needs_group_by_subq = $self->_has_attr (qw/prefetch distinct join seen_join group_by/); + my $needs_subq = $self->_has_attr (qw/row offset page/); + + if ($needs_group_by_subq or $needs_subq) { + + # make a new $rs selecting only the PKs (that's all we really need) + my $attrs = $self->_resolved_attrs_copy; + + delete $attrs->{$_} for qw/prefetch collapse select +select as +as columns +columns/; + $attrs->{columns} = [ map { "$attrs->{alias}.$_" } ($self->result_source->primary_columns) ]; + + if ($needs_group_by_subq) { + # make sure no group_by was supplied, or if there is one - make sure it matches + # the columns compiled above perfectly. Anything else can not be sanely executed + # on most databases so croak right then and there + + if (my $g = $attrs->{group_by}) { + my @current_group_by = map + { $_ =~ /\./ ? $_ : "$attrs->{alias}.$_" } + (ref $g eq 'ARRAY' ? @$g : $g ); + + if ( + join ("\x00", sort @current_group_by) + ne + join ("\x00", sort @{$attrs->{columns}} ) + ) { + $self->throw_exception ( + "You have just attempted a $op operation on a resultset which does group_by" + . ' on columns other than the primary keys, while DBIC internally needs to retrieve' + . ' the primary keys in a subselect. All sane RDBMS engines do not support this' + . ' kind of queries. Please retry the operation with a modified group_by or' + . ' without using one at all.' + ); + } + } + else { + $attrs->{group_by} = $attrs->{columns}; + } + } + + my $subrs = (ref $self)->new($rsrc, $attrs); -sub _update_delete_via_subq { - return $_[0]->_has_attr (qw/join seen_join group_by row offset page/); + return $self->result_source->storage->subq_update_delete($subrs, $op, $values); + } + else { + return $rsrc->storage->$op( + $rsrc, + $op eq 'update' ? $values : (), + $self->_cond_for_update_delete, + ); + } } @@ -1423,16 +1465,7 @@ sub update { $self->throw_exception('Values for update must be a hash') unless ref $values eq 'HASH'; - # rs operations with subqueries are Storage dependent - delegate - if ($self->_update_delete_via_subq) { - return $self->result_source->storage->subq_update_delete($self, 'update', $values); - } - - my $cond = $self->_cond_for_update_delete; - - return $self->result_source->storage->update( - $self->result_source, $values, $cond - ); + return $self->_rs_update_delete ('update', $values); } =head2 update_all @@ -1487,15 +1520,7 @@ sub delete { $self->throw_exception('delete does not accept any arguments') if @_; - # rs operations with subqueries are Storage dependent - delegate - if ($self->_update_delete_via_subq) { - return $self->result_source->storage->subq_update_delete($self, 'delete'); - } - - my $cond = $self->_cond_for_update_delete; - - $self->result_source->storage->delete($self->result_source, $cond); - return 1; + return $self->_rs_update_delete ('delete'); } =head2 delete_all @@ -2471,6 +2496,12 @@ sub _resolve_from { return ($from,$seen); } +# too many times we have to do $attrs = { %{$self->_resolved_attrs} } +sub _resolved_attrs_copy { + my $self = shift; + return { %{$self->_resolved_attrs (@_)} }; +} + sub _resolved_attrs { my $self = shift; return $self->{_attrs} if $self->{_attrs}; @@ -2571,7 +2602,7 @@ sub _resolved_attrs { } - $attrs->{group_by} ||= $attrs->{select} + $attrs->{group_by} ||= [ grep { !ref($_) || (ref($_) ne 'HASH') } @{$attrs->{select}} ] if delete $attrs->{distinct}; if ( $attrs->{order_by} ) { $attrs->{order_by} = (