From: Peter Rabbitson Date: Wed, 20 May 2009 07:03:08 +0000 (+0000) Subject: Make joined rs counts backwards compatible - we do not collapse a result exploded... X-Git-Tag: v0.08103~57 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=a258ee5dacc4e7e3c748f59e8335a7688f5455d0;p=dbsrgits%2FDBIx-Class.git Make joined rs counts backwards compatible - we do not collapse a result exploded by a has_many join unless it is explicitly requested by distinct => 1, OR unless we have collapse set which implies prefetch --- diff --git a/lib/DBIx/Class/ResultSet.pm b/lib/DBIx/Class/ResultSet.pm index fcbd2ef..19a71aa 100644 --- a/lib/DBIx/Class/ResultSet.pm +++ b/lib/DBIx/Class/ResultSet.pm @@ -1150,20 +1150,19 @@ on the resultset and counts the results of that. =cut -my @count_via_subq_attrs = qw/join seen_join prefetch group_by having/; sub count { my $self = shift; return $self->search(@_)->count if @_ and defined $_[0]; return scalar @{ $self->get_cache } if $self->get_cache; - my @check_attrs = @count_via_subq_attrs; + my @subq_attrs = qw/prefetch collapse group_by having/; # if we are not paged - we are simply asking for a limit if (not $self->{attrs}{page} and not $self->{attrs}{software_limit}) { - push @check_attrs, qw/rows offset/; + push @subq_attrs, qw/rows offset/; } - return $self->_has_attr (@check_attrs) + return $self->_has_attr (@subq_attrs) ? $self->_count_subq : $self->_count_simple } @@ -1187,7 +1186,7 @@ sub _count_subq { }]; # the subquery replaces this - delete $attrs->{where}; + delete $attrs->{$_} for qw/where bind prefetch collapse group_by having/; return $self->__count ($attrs); } @@ -1212,8 +1211,8 @@ sub __count { $attrs ||= { %{$self->{attrs}} }; - # these are the only attributes that actually matter for count - $attrs = { map { exists $attrs->{$_} ? ( $_ => $attrs->{$_} ) : () } qw/where bind alias from from_bind/ }; + # 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/); $attrs->{select} = { count => '*' }; $attrs->{as} = [qw/count/]; @@ -1852,8 +1851,21 @@ sub _has_attr { my $join_check_req; for my $n (@attr_names) { - return 1 if defined $attrs->{$n}; ++$join_check_req if $n =~ /join/; + + my $attr = $attrs->{$n}; + + next if not defined $attr; + + if (ref $attr eq 'HASH') { + return 1 if keys %$attr; + } + elsif (ref $attr eq 'ARRAY') { + return 1 if @$attr; + } + else { + return 1 if $attr; + } } # a join can be expressed as a multi-level from diff --git a/t/19quotes.t b/t/19quotes.t index 75b4668..622eefb 100644 --- a/t/19quotes.t +++ b/t/19quotes.t @@ -35,12 +35,9 @@ $rs = $schema->resultset('CD')->search( { join => 'artist' }); eval { $rs->count }; is_same_sql_bind( - $sql, - \@bind, - "SELECT COUNT( * ) FROM (SELECT `me`.`cdid` FROM `cd` `me` JOIN `artist` `artist` ON `artist`.`artistid` = `me`.`artist` WHERE ( ( `artist`.`name` = ? AND `me`.`year` = ? ) ) GROUP BY `me`.`cdid`) `count_subq`", - ["'Caterwauler McCrae'", "'2001'"], - - 'got correct SQL for joined count query with quoting' + $sql, \@bind, + "SELECT COUNT( * ) FROM `cd` `me` JOIN `artist` `artist` ON ( `artist`.`artistid` = `me`.`artist` ) WHERE ( `artist`.`name` = ? AND `me`.`year` = ? )", ["'Caterwauler McCrae'", "'2001'"], + 'got correct SQL for count query with quoting' ); my $order = 'year DESC'; @@ -62,10 +59,8 @@ $rs = $schema->resultset('CD')->search( { join => 'artist' }); eval { $rs->count }; is_same_sql_bind( - $sql, - \@bind, - "SELECT COUNT( * ) FROM (SELECT [me].[cdid] FROM [cd] [me] JOIN [artist] [artist] ON [artist].[artistid] = [me].[artist] WHERE ( ( [artist].[name] = ? AND [me].[year] = ? ) ) GROUP BY [me].[cdid]) [count_subq]", - ["'Caterwauler McCrae'", "'2001'"], + $sql, \@bind, + "SELECT COUNT( * ) FROM [cd] [me] JOIN [artist] [artist] ON ( [artist].[artistid] = [me].[artist] ) WHERE ( [artist].[name] = ? AND [me].[year] = ? )", ["'Caterwauler McCrae'", "'2001'"], 'got correct SQL for count query with bracket quoting' ); diff --git a/t/19quotes_newstyle.t b/t/19quotes_newstyle.t index 4e49117..80e6d04 100644 --- a/t/19quotes_newstyle.t +++ b/t/19quotes_newstyle.t @@ -41,10 +41,8 @@ $rs = $schema->resultset('CD')->search( { join => 'artist' }); eval { $rs->count }; is_same_sql_bind( - $sql, - \@bind, - "SELECT COUNT( * ) FROM (SELECT `me`.`cdid` FROM `cd` `me` JOIN `artist` `artist` ON `artist`.`artistid` = `me`.`artist` WHERE ( ( `artist`.`name` = ? AND `me`.`year` = ? ) ) GROUP BY `me`.`cdid`) `count_subq`", - ["'Caterwauler McCrae'", "'2001'"], + $sql, \@bind, + "SELECT COUNT( * ) FROM `cd` `me` JOIN `artist` `artist` ON ( `artist`.`artistid` = `me`.`artist` ) WHERE ( `artist`.`name` = ? AND `me`.`year` = ? )", ["'Caterwauler McCrae'", "'2001'"], 'got correct SQL for count query with quoting' ); @@ -74,10 +72,8 @@ $rs = $schema->resultset('CD')->search( { join => 'artist' }); eval { $rs->count }; is_same_sql_bind( - $sql, - \@bind, - "SELECT COUNT( * ) FROM (SELECT [me].[cdid] FROM [cd] [me] JOIN [artist] [artist] ON [artist].[artistid] = [me].[artist] WHERE ( ( [artist].[name] = ? AND [me].[year] = ? ) ) GROUP BY [me].[cdid]) [count_subq]", - ["'Caterwauler McCrae'", "'2001'"], + $sql, \@bind, + "SELECT COUNT( * ) FROM [cd] [me] JOIN [artist] [artist] ON ( [artist].[artistid] = [me].[artist] ) WHERE ( [artist].[name] = ? AND [me].[year] = ? )", ["'Caterwauler McCrae'", "'2001'"], 'got correct SQL for count query with bracket quoting' ); diff --git a/t/60core.t b/t/60core.t index a568c6e..042d543 100644 --- a/t/60core.t +++ b/t/60core.t @@ -8,7 +8,7 @@ use DBICTest; my $schema = DBICTest->init_schema(); -plan tests => 96; +plan tests => 98; eval { require DateTime::Format::MySQL }; my $NO_DTFM = $@ ? 1 : 0; @@ -221,16 +221,12 @@ my $search = [ { 'tags.tag' => 'Cheesy' }, { 'tags.tag' => 'Blue' } ]; my( $or_rs ) = $schema->resultset("CD")->search_rs($search, { join => 'tags', order_by => 'cdid' }); -# At this point in the test there are: -# 1 artist with the cheesy AND blue tag -# 1 artist with the cheesy tag -# 2 artists with the blue tag -# -# Formerly this test expected 5 as there was no collapsing of the AND condition -is($or_rs->count, 4, 'Search with OR ok'); +is($or_rs->all, 5, 'Joined search with OR returned correct number of rows'); +is($or_rs->count, 5, 'Search count with OR ok'); -my $distinct_rs = $schema->resultset("CD")->search($search, { join => 'tags', distinct => 1 }); -is($distinct_rs->all, 4, 'DISTINCT search with OR ok'); +my $collapsed_or_rs = $or_rs->search ({}, { distinct => 1 }); # induce collapse +is ($collapsed_or_rs->all, 4, 'Collapsed joined search with OR returned correct number of rows'); +is ($collapsed_or_rs->count, 4, 'Collapsed search count with OR ok'); { my $tcount = $schema->resultset('Track')->search( @@ -265,13 +261,7 @@ my $tag_rs = $schema->resultset('Tag')->search( my $rel_rs = $tag_rs->search_related('cd'); -# At this point in the test there are: -# 1 artist with the cheesy AND blue tag -# 1 artist with the cheesy tag -# 2 artists with the blue tag -# -# Formerly this test expected 5 as there was no collapsing of the AND condition -is($rel_rs->count, 4, 'Related search ok'); +is($rel_rs->count, 5, 'Related search ok'); is($or_rs->next->cdid, $rel_rs->next->cdid, 'Related object ok'); $or_rs->reset; diff --git a/t/90join_torture.t b/t/90join_torture.t index 494237f..0339bfc 100644 --- a/t/90join_torture.t +++ b/t/90join_torture.t @@ -46,23 +46,9 @@ cmp_ok(scalar @cds, '==', 1, "condition based on inherited join okay"); my $rs3 = $rs2->search_related('cds'); -# $rs3 selects * from cds_2, with the following join map -# -# artist -> cds_2 -# | -# V -# cds -> cd_to_producer -> producer -# | -# |\--> tags -# V -# tracks -# -# For some reason it is expected to return an exploded set of rows instead of the -# logical 3, even for a rowobject retrieval - why? -# cmp_ok(scalar($rs3->all), '==', 45, "All cds for artist returned"); -cmp_ok($rs3->count, '==', 3, "All cds for artist returned via count"); +cmp_ok($rs3->count, '==', 45, "All cds for artist returned via count"); my $rs4 = $schema->resultset("CD")->search({ 'artist.artistid' => '1' }, { join => ['tracks', 'artist'], prefetch => 'artist' }); my @rs4_results = $rs4->all; diff --git a/t/count/joined.t b/t/count/joined.t index 139f9cd..5ba7deb 100644 --- a/t/count/joined.t +++ b/t/count/joined.t @@ -7,11 +7,25 @@ use lib qw(t/lib); use DBICTest; -plan tests => 1; +plan tests => 3; my $schema = DBICTest->init_schema(); -{ - my $cds = $schema->resultset("CD")->search({ cdid => 1 }, { join => { cd_to_producer => 'producer' } }); - is($cds->count, 1, "extra joins do not explode single entity count"); -} +my $cds = $schema->resultset("CD")->search({ cdid => 1 }, { join => { cd_to_producer => 'producer' } }); +cmp_ok($cds->count, '>', 1, "extra joins explode entity count"); + +is ( + $cds->search({}, { prefetch => 'cd_to_producer' })->count, + 1, + "Count correct with extra joins collapsed by prefetch" +); + +is ( + $cds->search({}, { distinct => 1 })->count, + 1, + "Count correct with requested distinct collapse of main table" +); + + + +