X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FResultSet.pm;h=4e06ed6c34f9512626b8ce5d220dc00686221795;hb=f50497ab497520c6f79154cdff283921c4d2cb9e;hp=4e399a328c3228558488fb1c568fba53f2d260b8;hpb=4fff7913ca44081e9c3d901912fdfba5e125d3c7;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/ResultSet.pm b/lib/DBIx/Class/ResultSet.pm index 4e399a3..4e06ed6 100644 --- a/lib/DBIx/Class/ResultSet.pm +++ b/lib/DBIx/Class/ResultSet.pm @@ -25,6 +25,10 @@ DBIx::Class::ResultSet - Represents a query used for fetching a set of results. =head1 SYNOPSIS my $users_rs = $schema->resultset('User'); + while( $user = $users_rs->next) { + print $user->username; + } + my $registered_users_rs = $schema->resultset('User')->search({ registered => 1 }); my @cds_in_2005 = $schema->resultset('CD')->search({ year => 2005 })->all(); @@ -535,7 +539,7 @@ sub find { } # Run the query - my $rs = $self->search ($query, {result_class => $self->result_class, %$attrs}); + my $rs = $self->search ($query, $attrs); if (keys %{$rs->_resolved_attrs->{collapse}}) { my $row = $rs->next; carp "Query returned more than one row" if $rs->next; @@ -1003,7 +1007,7 @@ sub _collapse_result { # without having to contruct the full hash if (keys %collapse) { - my %pri = map { ($_ => 1) } $self->result_source->primary_columns; + my %pri = map { ($_ => 1) } $self->result_source->_pri_cols; foreach my $i (0 .. $#construct_as) { next if defined($construct_as[$i][0]); # only self table if (delete $pri{$construct_as[$i][1]}) { @@ -1134,8 +1138,14 @@ in the original source class will not run. sub result_class { my ($self, $result_class) = @_; if ($result_class) { - $self->ensure_class_loaded($result_class); + unless (ref $result_class) { # don't fire this for an object + $self->ensure_class_loaded($result_class); + } $self->_result_class($result_class); + # THIS LINE WOULD BE A BUG - this accessor specifically exists to + # permit the user to set result class on one result set only; it only + # chains if provided to search() + #$self->{attrs}{result_class} = $result_class if ref $self; } $self->_result_class; } @@ -1230,10 +1240,11 @@ sub _count_rs { my $rsrc = $self->result_source; $attrs ||= $self->_resolved_attrs; - my $tmp_attrs = { %$attrs }; - - # take off any limits, record_filter is cdbi, and no point of ordering a count - delete $tmp_attrs->{$_} for (qw/select as rows offset order_by record_filter/); + # only take pieces we need for a simple count + my $tmp_attrs = { map + { $_ => $attrs->{$_} } + qw/ alias from where bind join / + }; # overwrite the selector (supplied by the storage) $tmp_attrs->{select} = $rsrc->storage->_count_select ($rsrc, $tmp_attrs); @@ -1251,37 +1262,43 @@ sub _count_subq_rs { my ($self, $attrs) = @_; my $rsrc = $self->result_source; - $attrs ||= $self->_resolved_attrs_copy; - - my $sub_attrs = { %$attrs }; + $attrs ||= $self->_resolved_attrs; - # extra selectors do not go in the subquery and there is no point of ordering it - delete $sub_attrs->{$_} for qw/collapse select _prefetch_select as order_by/; + my $sub_attrs = { map + { $_ => $attrs->{$_} } + qw/ alias from where bind join group_by having rows offset / + }; # if we multi-prefetch we group_by primary keys only as this is what we would # get out of the rs via ->next/->all. We *DO WANT* to clobber old group_by regardless if ( keys %{$attrs->{collapse}} ) { - $sub_attrs->{group_by} = [ map { "$attrs->{alias}.$_" } ($rsrc->primary_columns) ] + $sub_attrs->{group_by} = [ map { "$attrs->{alias}.$_" } ($rsrc->_pri_cols) ] } - $sub_attrs->{select} = $rsrc->storage->_subq_count_select ($rsrc, $attrs); + # Calculate subquery selector + if (my $g = $sub_attrs->{group_by}) { - # this is so that the query can be simplified e.g. - # * ordering can be thrown away in things like Top limit - $sub_attrs->{-for_count_only} = 1; - - my $sub_rs = $rsrc->resultset_class->new ($rsrc, $sub_attrs); - - $attrs->{from} = [{ - -alias => 'count_subq', - -source_handle => $rsrc->handle, - count_subq => $sub_rs->as_query, - }]; + # necessary as the group_by may refer to aliased functions + my $sel_index; + for my $sel (@{$attrs->{select}}) { + $sel_index->{$sel->{-as}} = $sel + if (ref $sel eq 'HASH' and $sel->{-as}); + } - # the subquery replaces this - delete $attrs->{$_} for qw/where bind collapse group_by having having_bind rows offset/; + for my $g_part (@$g) { + push @{$sub_attrs->{select}}, $sel_index->{$g_part} || $g_part; + } + } + else { + my @pcols = map { "$attrs->{alias}.$_" } ($rsrc->primary_columns); + $sub_attrs->{select} = @pcols ? \@pcols : [ 1 ]; + } - return $self->_count_rs ($attrs); + return $rsrc->resultset_class + ->new ($rsrc, $sub_attrs) + ->as_subselect_rs + ->search ({}, { columns => { count => $rsrc->storage->_count_select ($rsrc, $attrs) } }) + -> get_column ('count'); } sub _bool { @@ -1412,7 +1429,7 @@ sub _rs_update_delete { my $cond = $rsrc->schema->storage->_strip_cond_qualifiers ($self->{cond}); my $needs_group_by_subq = $self->_has_resolved_attr (qw/collapse group_by -join/); - my $needs_subq = $needs_group_by_subq || (not defined $cond) || $self->_has_resolved_attr(qw/row offset/); + my $needs_subq = $needs_group_by_subq || (not defined $cond) || $self->_has_resolved_attr(qw/rows offset/); if ($needs_group_by_subq or $needs_subq) { @@ -1420,7 +1437,7 @@ sub _rs_update_delete { my $attrs = $self->_resolved_attrs_copy; delete $attrs->{$_} for qw/collapse select as/; - $attrs->{columns} = [ map { "$attrs->{alias}.$_" } ($self->result_source->primary_columns) ]; + $attrs->{columns} = [ map { "$attrs->{alias}.$_" } ($self->result_source->_pri_cols) ]; if ($needs_group_by_subq) { # make sure no group_by was supplied, or if there is one - make sure it matches @@ -1508,9 +1525,10 @@ sub update_all { my ($self, $values) = @_; $self->throw_exception('Values for update_all must be a hash') unless ref $values eq 'HASH'; - foreach my $obj ($self->all) { - $obj->set_columns($values)->update; - } + + my $guard = $self->result_source->schema->txn_scope_guard; + $_->update($values) for $self->all; + $guard->commit; return 1; } @@ -1528,7 +1546,7 @@ Deletes the contents of the resultset from its result source. Note that this will not run DBIC cascade triggers. See L if you need triggers to run. See also L. -Return value will be the amount of rows deleted; exact type of return value +Return value will be the number of rows deleted; exact type of return value is storage-dependent. =cut @@ -1561,7 +1579,9 @@ sub delete_all { $self->throw_exception('delete_all does not accept any arguments') if @_; + my $guard = $self->result_source->schema->txn_scope_guard; $_->delete for $self->all; + $guard->commit; return 1; } @@ -2290,7 +2310,7 @@ For example: producer => $producer, name => 'harry', }, { - key => 'primary, + key => 'primary', }); @@ -2660,16 +2680,26 @@ but because we isolated the group by into a subselect the above works. =cut sub as_subselect_rs { - my $self = shift; + my $self = shift; + + my $attrs = $self->_resolved_attrs; + + my $fresh_rs = (ref $self)->new ( + $self->result_source + ); - return $self->result_source->resultset->search( undef, { - alias => $self->current_source_alias, - from => [{ - $self->current_source_alias => $self->as_query, - -alias => $self->current_source_alias, - -source_handle => $self->result_source->handle, - }] - }); + # these pieces will be locked in the subquery + delete $fresh_rs->{cond}; + delete @{$fresh_rs->{attrs}}{qw/where bind/}; + + return $fresh_rs->search( {}, { + from => [{ + $attrs->{alias} => $self->as_query, + -alias => $attrs->{alias}, + -source_handle => $self->result_source->handle, + }], + alias => $attrs->{alias}, + }); } # This code is called by search_related, and makes sure there @@ -2694,7 +2724,7 @@ sub _chain_relationship { # ->_resolve_join as otherwise they get lost - captainL my $join = $self->_merge_attr( $attrs->{join}, $attrs->{prefetch} ); - delete @{$attrs}{qw/join prefetch collapse distinct select as columns +select +as +columns/}; + delete @{$attrs}{qw/join prefetch collapse group_by distinct select as columns +select +as +columns/}; my $seen = { %{ (delete $attrs->{seen_join}) || {} } }; @@ -2720,7 +2750,7 @@ sub _chain_relationship { -alias => $attrs->{alias}, $attrs->{alias} => $rs_copy->as_query, }]; - delete @{$attrs}{@force_subq_attrs, 'where'}; + delete @{$attrs}{@force_subq_attrs, qw/where bind/}; $seen->{-relation_chain_depth} = 0; } elsif ($attrs->{from}) { #shallow copy suffices @@ -3254,23 +3284,27 @@ names: select => [ 'name', { count => 'employeeid' }, - { sum => 'salary' } + { max => { length => 'name' }, -as => 'longest_name' } ] }); -When you use function/stored procedure names and do not supply an C -attribute, the column names returned are storage-dependent. E.g. MySQL would -return a column named C in the above example. + # Equivalent SQL + SELECT name, COUNT( employeeid ), MAX( LENGTH( name ) ) AS longest_name FROM employee -B You will almost always need a corresponding 'as' entry when you use -'select'. +B You will almost always need a corresponding L attribute when you +use L, to instruct DBIx::Class how to store the result of the column. +Also note that the L attribute has nothing to do with the SQL-side 'AS' +identifier aliasing. You can however alias a function, so you can use it in +e.g. an C clause. This is done via the C<-as> B but adds columns to the selection. +L but adds columns to the default selection, instead of specifying +an explicit list. =back @@ -3290,25 +3324,26 @@ Indicates additional column names for those added via L. See L. =back -Indicates column names for object inflation. That is, C -indicates the name that the column can be accessed as via the -C method (or via the object accessor, B). It has nothing to do with the SQL code C, -usually when C for details. $rs = $schema->resultset('Employee')->search(undef, { select => [ 'name', - { count => 'employeeid' } + { count => 'employeeid' }, + { max => { length => 'name' }, -as => 'longest_name' } ], - as => ['name', 'employee_count'], + as => [qw/ + name + employee_count + max_name_length + /], }); - my $employee = $rs->first(); # get the first Employee - If the object against which the search is performed already has an accessor matching a column name specified in C, the value can be retrieved using the accessor as normal: @@ -3323,16 +3358,6 @@ use C instead: You can create your own accessors if required - see L for details. -Please note: This will NOT insert an C into the SQL -statement produced, it is used for internal access only. Thus -attempting to use the accessor in an C clause or similar -will fail miserably. - -To get around this limitation, you can supply literal SQL to your -C