Make joined rs counts backwards compatible - we do not collapse a result exploded...
Peter Rabbitson [Wed, 20 May 2009 07:03:08 +0000 (07:03 +0000)]
lib/DBIx/Class/ResultSet.pm
t/19quotes.t
t/19quotes_newstyle.t
t/60core.t
t/90join_torture.t
t/count/joined.t

index fcbd2ef..19a71aa 100644 (file)
@@ -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
index 75b4668..622eefb 100644 (file)
@@ -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'
 );
 
index 4e49117..80e6d04 100644 (file)
@@ -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'
 );
 
index a568c6e..042d543 100644 (file)
@@ -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;
index 494237f..0339bfc 100644 (file)
@@ -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;
index 139f9cd..5ba7deb 100644 (file)
@@ -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"
+);
+
+
+
+