From: Peter Rabbitson Date: Sat, 20 Jun 2009 21:30:10 +0000 (+0000) Subject: More tests and a really working count_rs X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=a70f69d18fcc84b736103456a2e4bea5144212cc;p=dbsrgits%2FDBIx-Class-Historic.git More tests and a really working count_rs --- diff --git a/Changes b/Changes index 0ff9a38..3bd0d4f 100644 --- a/Changes +++ b/Changes @@ -5,6 +5,8 @@ Revision history for DBIx::Class - Fixed HRI returning too many empty results on multilevel nonexisting prefetch - Fixed the prefetch with limit bug + - New resultsed method count_rs, returns a ::ResultSetColumn + returning a single count value 0.08107 2009-06-14 08:21:00 (UTC) - Fix serialization regression introduced in 0.08103 (affects diff --git a/lib/DBIx/Class/Storage/DBI.pm b/lib/DBIx/Class/Storage/DBI.pm index 707b305..7998423 100644 --- a/lib/DBIx/Class/Storage/DBI.pm +++ b/lib/DBIx/Class/Storage/DBI.pm @@ -1296,6 +1296,7 @@ sub _adjust_select_args_for_limited_prefetch { $self->throw_exception ('Prefetch with limit (rows/offset) is not supported on resultsets with a custom from attribute') if (ref $from ne 'ARRAY'); + # separate attributes my $sub_attrs = { %$attrs }; delete $attrs->{$_} for qw/where bind rows offset/; @@ -1314,13 +1315,31 @@ sub _adjust_select_args_for_limited_prefetch { ]; } + # mangle {from} + $from = [ @$from ]; + my $select_root = shift @$from; + my @outer_from = @$from; - # mangle the head of the {from} - my $self_ident = shift @$from; - + my %inner_joins; my %join_info = map { $_->[0]{-alias} => $_->[0] } (@$from); - my (%inner_joins); + # in complex search_related chains $alias may *not* be 'me' + # so always include it in the inner join, and also shift away + # from the outer stack, so that the two datasets actually do + # meet + if ($select_root->{-alias} ne $alias) { + $inner_joins{$alias} = 1; + + while (@outer_from && $outer_from[0][0]{-alias} ne $alias) { + shift @outer_from; + } + if (! @outer_from) { + $self->throw_exception ("Unable to find '$alias' in the {from} stack, something is wrong"); + } + + shift @outer_from; # the new subquery will represent this alias, so get rid of it + } + # decide which parts of the join will remain on the inside # @@ -1379,22 +1398,21 @@ sub _adjust_select_args_for_limited_prefetch { } # construct the inner $from for the subquery - my $inner_from = [ $self_ident ]; - if (keys %inner_joins) { - for my $j (@$from) { - push @$inner_from, $j if $inner_joins{$j->[0]{-alias}}; - } + my $inner_from = [ $select_root ]; + for my $j (@$from) { + push @$inner_from, $j if $inner_joins{$j->[0]{-alias}}; + } - # if a multi-type join was needed in the subquery ("multi" is indicated by - # presence in {collapse}) - add a group_by to simulate the collapse in the subq - for my $alias (keys %inner_joins) { + # if a multi-type join was needed in the subquery ("multi" is indicated by + # presence in {collapse}) - add a group_by to simulate the collapse in the subq - # the dot comes from some weirdness in collapse - # remove after the rewrite - if ($attrs->{collapse}{".$alias"}) { - $sub_attrs->{group_by} = $sub_select; - last; - } + for my $alias (keys %inner_joins) { + + # the dot comes from some weirdness in collapse + # remove after the rewrite + if ($attrs->{collapse}{".$alias"}) { + $sub_attrs->{group_by} = $sub_select; + last; } } @@ -1406,14 +1424,14 @@ sub _adjust_select_args_for_limited_prefetch { $sub_attrs ); - # put it back in $from - unshift @$from, { $alias => $subq }; + # put it in the new {from} + unshift @outer_from, { $alias => $subq }; # This is totally horrific - the $where ends up in both the inner and outer query # Unfortunately not much can be done until SQLA2 introspection arrives # # OTOH it can be seen as a plus: (notes that this query would make a DBA cry ;) - return ($from, $select, $where, $attrs); + return (\@outer_from, $select, $where, $attrs); } sub _resolve_ident_sources { diff --git a/t/count/count_rs.t b/t/count/count_rs.t new file mode 100644 index 0000000..7153d3e --- /dev/null +++ b/t/count/count_rs.t @@ -0,0 +1,119 @@ +use strict; +use warnings; + +use lib qw(t/lib); + +use Test::More; +use DBICTest; +use DBIC::SqlMakerTest; +use DBIC::DebugObj; + +plan tests => 10; + +my $schema = DBICTest->init_schema(); + +# non-collapsing prefetch (no multi prefetches) +{ + my $rs = $schema->resultset("CD") + ->search_related('tracks', + { position => [1,2] }, + { prefetch => [qw/disc lyrics/], rows => 3, offset => 8 }, + ); + is ($rs->all, 2, 'Correct number of objects'); + + + my ($sql, @bind); + $schema->storage->debugobj(DBIC::DebugObj->new(\$sql, \@bind)); + $schema->storage->debug(1); + + is ($rs->count, 2, 'Correct count via count()'); + + is_same_sql_bind ( + $sql, + \@bind, + 'SELECT COUNT( * ) + FROM cd me + LEFT JOIN track tracks ON tracks.cd = me.cdid + JOIN cd disc ON disc.cdid = tracks.cd + LEFT JOIN lyrics lyrics ON lyrics.track_id = tracks.trackid + WHERE ( ( position = ? OR position = ? ) ) + ', + [ qw/'1' '2'/ ], + 'count softlimit applied', + ); + + my $crs = $rs->count_rs; + is ($crs->next, 2, 'Correct count via count_rs()'); + + is_same_sql_bind ( + $crs->as_query, + '(SELECT COUNT( * ) + FROM ( + SELECT tracks.trackid + FROM cd me + LEFT JOIN track tracks ON tracks.cd = me.cdid + JOIN cd disc ON disc.cdid = tracks.cd + LEFT JOIN lyrics lyrics ON lyrics.track_id = tracks.trackid + WHERE ( ( position = ? OR position = ? ) ) + LIMIT 3 OFFSET 8 + ) count_subq + )', + [ [ position => 1 ], [ position => 2 ] ], + 'count_rs db-side limit applied', + ); +} + +# has_many prefetch with limit +{ + my $rs = $schema->resultset("Artist") + ->search_related('cds', + { 'tracks.position' => [1,2] }, + { prefetch => [qw/tracks artist/], rows => 3, offset => 4 }, + ); + is ($rs->all, 1, 'Correct number of objects'); + + my ($sql, @bind); + $schema->storage->debugobj(DBIC::DebugObj->new(\$sql, \@bind)); + $schema->storage->debug(1); + + is ($rs->count, 1, 'Correct count via count()'); + + is_same_sql_bind ( + $sql, + \@bind, + 'SELECT COUNT( * ) + FROM ( + SELECT cds.cdid + FROM artist me + LEFT JOIN cd cds ON cds.artist = me.artistid + LEFT JOIN track tracks ON tracks.cd = cds.cdid + JOIN artist artist ON artist.artistid = cds.artist + WHERE tracks.position = ? OR tracks.position = ? + GROUP BY cds.cdid + ) count_subq + ', + [ qw/'1' '2'/ ], + 'count softlimit applied', + ); + + my $crs = $rs->count_rs; + is ($crs->next, 1, 'Correct count via count_rs()'); + + is_same_sql_bind ( + $crs->as_query, + '(SELECT COUNT( * ) + FROM ( + SELECT cds.cdid + FROM artist me + LEFT JOIN cd cds ON cds.artist = me.artistid + LEFT JOIN track tracks ON tracks.cd = cds.cdid + JOIN artist artist ON artist.artistid = cds.artist + WHERE tracks.position = ? OR tracks.position = ? + GROUP BY cds.cdid + LIMIT 3 OFFSET 4 + ) count_subq + )', + [ [ 'tracks.position' => 1 ], [ 'tracks.position' => 2 ] ], + 'count_rs db-side limit applied', + ); +}