From: Peter Rabbitson Date: Tue, 30 Jun 2009 14:23:06 +0000 (+0000) Subject: rs->get_column now properly recognizes prefetch and collapses if at all possible X-Git-Tag: v0.08108~37^2~2 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=dbsrgits%2FDBIx-Class.git;a=commitdiff_plain;h=d8dbe471a2f98a0bc2de5e629f22ee2f2ec034ef rs->get_column now properly recognizes prefetch and collapses if at all possible --- diff --git a/lib/DBIx/Class/ResultSetColumn.pm b/lib/DBIx/Class/ResultSetColumn.pm index 037b771..1596e53 100644 --- a/lib/DBIx/Class/ResultSetColumn.pm +++ b/lib/DBIx/Class/ResultSetColumn.pm @@ -39,27 +39,48 @@ sub new { $rs->throw_exception("column must be supplied") unless $column; - my $new_parent_rs = $rs->search_rs; # we don't want to mess up the original, so clone it + my $orig_attrs = $rs->_resolved_attrs; + my $new_parent_rs = $rs->search_rs; # prefetch causes additional columns to be fetched, but we can not just make a new # rs via the _resolved_attrs trick - we need to retain the separation between # +select/+as and select/as. At the same time we want to preserve any joins that the # prefetch would otherwise generate. - my $init_attrs = $new_parent_rs->{attrs} ||= {}; - delete $init_attrs->{collapse}; - $init_attrs->{join} = $rs->_merge_attr( delete $init_attrs->{join}, delete $init_attrs->{prefetch} ); + + my $new_attrs = $new_parent_rs->{attrs} ||= {}; + $new_attrs->{join} = $rs->_merge_attr( delete $new_attrs->{join}, delete $new_attrs->{prefetch} ); # If $column can be found in the 'as' list of the parent resultset, use the # corresponding element of its 'select' list (to keep any custom column # definition set up with 'select' or '+select' attrs), otherwise use $column # (to create a new column definition on-the-fly). - my $attrs = $new_parent_rs->_resolved_attrs; - my $as_list = $attrs->{as} || []; - my $select_list = $attrs->{select} || []; + my $as_list = $orig_attrs->{as} || []; + my $select_list = $orig_attrs->{select} || []; my $as_index = List::Util::first { ($as_list->[$_] || "") eq $column } 0..$#$as_list; my $select = defined $as_index ? $select_list->[$as_index] : $column; + # {collapse} would mean a has_many join was injected, which in turn means + # we need to group IF WE CAN (only if the column in question is unique) + if (!$new_attrs->{group_by} && keys %{$orig_attrs->{collapse}}) { + + # scan for a constraint that would contain our column only - that'd be proof + # enough it is unique + my $constraints = { $rs->result_source->unique_constraints }; + for my $constraint_columns ( values %$constraints ) { + + next unless @$constraint_columns == 1; + + my $col = $constraint_columns->[0]; + my $fqcol = join ('.', $new_attrs->{alias}, $col); + + if ($col eq $select or $fqcol eq $select) { + $new_attrs->{group_by} = [ $select ]; + last; + } + } + } + my $new = bless { _select => $select, _as => $column, _parent_resultset => $new_parent_rs }, $class; return $new; } diff --git a/t/88result_set_column.t b/t/88result_set_column.t index 66169f3..aac98dc 100644 --- a/t/88result_set_column.t +++ b/t/88result_set_column.t @@ -8,10 +8,9 @@ use DBICTest; my $schema = DBICTest->init_schema(); -plan tests => 18; +plan tests => 20; -my $cd; -my $rs = $cd = $schema->resultset("CD")->search({}, { order_by => 'cdid' }); +my $rs = $schema->resultset("CD")->search({}, { order_by => 'cdid' }); my $rs_title = $rs->get_column('title'); my $rs_year = $rs->get_column('year'); @@ -76,3 +75,22 @@ is ($schema->resultset('BooksInLibrary')->get_column ('price')->sum, 125, 'Sum o my $owner = $schema->resultset('Owners')->find ({ name => 'Newton' }); ok ($owner->books->count > 1, 'Owner Newton has multiple books'); is ($owner->search_related ('books')->get_column ('price')->sum, 60, 'Correctly calculated price of all owned books'); + + +# make sure joined/prefetched get_column of a PK dtrt + +$rs->reset; +my $j_rs = $rs->search ({}, { join => 'tracks' })->get_column ('cdid'); +is_deeply ( + [ $j_rs->all ], + [ map { my $c = $rs->next; ( ($c->id) x $c->tracks->count ) } (1 .. $rs->count) ], + 'join properly explodes amount of rows from get_column', +); + +$rs->reset; +my $p_rs = $rs->search ({}, { prefetch => 'tracks' })->get_column ('cdid'); +is_deeply ( + [ $p_rs->all ], + [ $rs->get_column ('cdid')->all ], + 'prefetch properly collapses amount of rows from get_column', +); diff --git a/t/prefetch/grouped.t b/t/prefetch/grouped.t index 85fd730..3db5f9e 100644 --- a/t/prefetch/grouped.t +++ b/t/prefetch/grouped.t @@ -77,7 +77,7 @@ for ($cd_rs->all) { SELECT me.cd FROM track me JOIN cd cd ON cd.cdid = me.cd - WHERE ( me.cd IN ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) ) + WHERE ( me.cd IN ( ?, ?, ?, ?, ? ) ) GROUP BY me.cd ) count_subq @@ -94,11 +94,11 @@ for ($cd_rs->all) { SELECT me.cd, COUNT (me.trackid) AS track_count, FROM track me JOIN cd cd ON cd.cdid = me.cd - WHERE ( me.cd IN ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) ) + WHERE ( me.cd IN ( ?, ?, ?, ?, ? ) ) GROUP BY me.cd ) as me JOIN cd cd ON cd.cdid = me.cd - WHERE ( me.cd IN ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) ) + WHERE ( me.cd IN ( ?, ?, ?, ?, ? ) ) )', [ map { [ 'me.cd' => $_] } ( ($cd_rs->get_column ('cdid')->all) x 2 ) ], 'next() query generated expected SQL',