Ensure collapse is respected regardless of selection type
Peter Rabbitson [Sun, 27 Jul 2014 12:13:45 +0000 (14:13 +0200)]
Switch the attribute name/logic (passthrough on explicit presence only)

Changes
lib/DBIx/Class/ResultSet.pm
lib/DBIx/Class/Storage/DBI.pm
lib/DBIx/Class/Storage/DBIHacks.pm
t/count/joined.t

diff --git a/Changes b/Changes
index 9932b08..2aebd6d 100644 (file)
--- a/Changes
+++ b/Changes
@@ -21,6 +21,7 @@ Revision history for DBIx::Class
         - Fix on_connect_* not always firing in some cases - a race condition
           existed between storage accessor setters and the determine_driver
           routines, triggering a connection before the set-cycle is finished
+        - Fix collapse being ignored on single-origin selection (RT#95658)
         - Fix failure to detect stable order criteria when in iterator
           mode of a has_many prefetch off a search_related chain
         - Prevent erroneous database hit when accessing prefetched related
index b703c8f..44adfe8 100644 (file)
@@ -312,7 +312,7 @@ sub new {
     if $source->isa('DBIx::Class::ResultSourceHandle');
 
   $attrs = { %{$attrs||{}} };
-  delete @{$attrs}{qw(_last_sqlmaker_alias_map _related_results_construction)};
+  delete @{$attrs}{qw(_last_sqlmaker_alias_map _simple_passthrough_construction)};
 
   if ($attrs->{page}) {
     $attrs->{rows} ||= 10;
@@ -1415,8 +1415,8 @@ sub _construct_results {
   ) ? 1 : 0 ) unless defined $self->{_result_inflator}{is_hri};
 
 
-  if (! $attrs->{_related_results_construction}) {
-    # construct a much simpler array->hash folder for the one-table cases right here
+  if ($attrs->{_simple_passthrough_construction}) {
+    # construct a much simpler array->hash folder for the one-table HRI cases right here
     if ($self->{_result_inflator}{is_hri}) {
       for my $r (@$rows) {
         $r = { map { $infmap->[$_] => $r->[$_] } 0..$#$infmap };
@@ -3749,10 +3749,11 @@ sub _resolved_attrs {
   push @{$attrs->{select}}, @prefetch_select;
   push @{$attrs->{as}}, @prefetch_as;
 
-  # whether we can get away with the dumbest (possibly DBI-internal) collapser
-  if ( List::Util::first { $_ =~ /\./ } @{$attrs->{as}} ) {
-    $attrs->{_related_results_construction} = 1;
-  }
+  $attrs->{_simple_passthrough_construction} = !(
+    $attrs->{collapse}
+      or
+    grep { $_ =~ /\./ } @{$attrs->{as}}
+  );
 
   # if both page and offset are specified, produce a combined offset
   # even though it doesn't make much sense, this is what pre 081xx has
index 18dbbb9..139dc49 100644 (file)
@@ -2469,7 +2469,7 @@ sub _select_args {
     # are happy (this includes MySQL in strict_mode)
     # If any of the other joined tables are referenced in the group_by
     # however - the user is on their own
-    ( $prefetch_needs_subquery or $attrs->{_related_results_construction} )
+    ( $prefetch_needs_subquery or ! $attrs->{_simple_passthrough_construction} )
       and
     $attrs->{group_by}
       and
index 26f8dca..c7910be 100644 (file)
@@ -111,8 +111,8 @@ sub _adjust_select_args_for_complex_prefetch {
   my $outer_attrs = { %$attrs };
   delete @{$outer_attrs}{qw(from bind rows offset group_by _grouped_by_distinct having)};
 
-  my $inner_attrs = { %$attrs };
-  delete @{$inner_attrs}{qw(for collapse select as _related_results_construction)};
+  my $inner_attrs = { %$attrs, _simple_passthrough_construction => 1 };
+  delete @{$inner_attrs}{qw(for collapse select as)};
 
   # there is no point of ordering the insides if there is no limit
   delete $inner_attrs->{order_by} if (
index 352eef9..bb8eb4c 100644 (file)
@@ -7,24 +7,37 @@ use lib qw(t/lib);
 
 use DBICTest;
 
-plan tests => 7;
-
 my $schema = DBICTest->init_schema();
 
 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"
-);
+for my $arg (
+  [ 'prefetch-collapsed has_many' => { prefetch => 'cd_to_producer' } ],
+  [ 'distict-collapsed result' => { distinct => 1 } ],
+  [ 'explicit collapse request' => { collapse => 1 } ],
+) {
+  for my $hri (0,1) {
+    my $diag = $arg->[0] . ($hri ? ' with HRI' : '');
+
+    my $rs = $cds->search({}, {
+      %{$arg->[1]},
+      $hri ? ( result_class => 'DBIx::Class::ResultClass::HashRefInflator' ) : (),
+    });
+
+    is
+      $rs->count,
+      1,
+      "Count correct on $diag",
+    ;
+
+    is
+      scalar $rs->all,
+      1,
+      "Amount of constructed objects matches count on $diag",
+    ;
+  }
+}
 
 # JOIN and LEFT JOIN issues mean that we've seen problems where counted rows and fetched rows are sometimes 1 higher than they should
 # be in the related resultset.
@@ -35,3 +48,5 @@ is(scalar($artist->related_resultset('cds')->all()), 0, "No CDs fetched for a sh
 my $artist_rs = $schema->resultset('Artist')->search({artistid => $artist->id});
 is($artist_rs->related_resultset('cds')->count(), 0, "No CDs counted for a shiny new artist using a resultset search");
 is(scalar($artist_rs->related_resultset('cds')->all), 0, "No CDs fetched for a shiny new artist using a resultset search");
+
+done_testing;