Explicitly disallow redirection of has_many columns to main object
Peter Rabbitson [Fri, 19 Apr 2013 12:32:06 +0000 (14:32 +0200)]
This can never work in practice, but it is too obscure to have a user catch it

lib/DBIx/Class/ResultSet.pm
t/prefetch/manual.t
t/resultset/inflatemap_abuse.t

index af1cbe6..df7b701 100644 (file)
@@ -1346,6 +1346,36 @@ sub _construct_results {
 
   return undef unless @{$rows||[]};
 
+  # sanity check - people are too clever for their own good
+  if ($attrs->{collapse} and my $aliastypes = $attrs->{_sqlmaker_select_args}[3]{_aliastypes} ) {
+
+    my $multiplied_selectors;
+    for my $sel_alias ( grep { $_ ne $attrs->{alias} } keys %{ $aliastypes->{selecting} } ) {
+      if (
+        $aliastypes->{multiplying}{$sel_alias}
+          or
+        scalar grep { $aliastypes->{multiplying}{(values %$_)[0]} } @{ $aliastypes->{selecting}{$sel_alias}{-parents} }
+      ) {
+        $multiplied_selectors->{$_} = 1 for values %{$aliastypes->{selecting}{$sel_alias}{-seen_columns}}
+      }
+    }
+
+    for my $i (0 .. $#{$attrs->{as}} ) {
+      my $sel = $attrs->{select}[$i];
+
+      if (ref $sel eq 'SCALAR') {
+        $sel = $$sel;
+      }
+      elsif( ref $sel eq 'REF' and ref $$sel eq 'ARRAY' ) {
+        $sel = $$sel->[0];
+      }
+
+      $self->throw_exception(
+        'Result collapse not possible - selection from a has_many source redirected to the main object'
+      ) if ($multiplied_selectors->{$sel} and $attrs->{as}[$i] !~ /\./);
+    }
+  }
+
   my @extra_collapser_args;
   if ($attrs->{collapse} and ! $did_fetch_all ) {
 
@@ -3523,9 +3553,6 @@ sub _resolved_attrs {
   if ( List::Util::first { $_ =~ /\./ } @{$attrs->{as}} ) {
     $attrs->{_related_results_construction} = 1;
   }
-  else {
-    $attrs->{collapse} = 0;
-  }
 
   # run through the resulting joinstructure (starting from our current slot)
   # and unset collapse if proven unnesessary
index 1ad2253..97f45c9 100644 (file)
@@ -220,29 +220,6 @@ lives_ok { my $dummy = $rs;  warnings_exist {
 ], 'expected_warnings'
 } 'traversing prefetch chain with empty intermediates works';
 
-TODO: {
-local $TODO = 'this does not work at all, need to promote rsattrs to an object on its own';
-# make sure has_many column redirection does not do weird stuff when collapse is requested
-for my $pref_args (
-  { prefetch => 'cds'},
-  { collapse => 1 }
-) {
-  for my $col_and_join_args (
-    { '+columns' => { 'cd_title' => 'cds_2.title' }, join => [ 'cds', 'cds' ] },
-    { '+columns' => { 'cd_title' => 'cds.title' }, join => 'cds', }
-  ) {
-
-    my $weird_rs = $schema->resultset('Artist')->search({}, {
-      %$col_and_join_args, %$pref_args,
-    });
-
-    for (qw/next all first/) {
-      throws_ok { $weird_rs->$_ } qr/not yet determined exception text/;
-    }
-  }
-}
-}
-
 # multi-has_many with underdefined root, with rather random order
 $rs = $schema->resultset ('CD')->search ({}, {
   join => [ 'tracks', { single_track => { cd => { artist => { cds => 'tracks' } } } }  ],
index 1be2f69..1645ca1 100644 (file)
@@ -70,4 +70,28 @@ throws_ok
   'Correct exception on illegal ::Row inflation attempt'
 ;
 
+# make sure has_many column redirection does not do weird stuff when collapse is requested
+for my $pref_args (
+  { prefetch => 'cds'},
+  { collapse => 1 }
+) {
+  for my $col_and_join_args (
+    { '+columns' => { 'cd_title' => 'cds_2.title' }, join => [ 'cds', 'cds' ] },
+    { '+columns' => { 'cd_title' => 'cds.title' }, join => 'cds' },
+    { '+columns' => { 'cd_gr_name' => 'genre.name' }, join => { cds => 'genre' } },
+  ) {
+    for my $call (qw(next all first)) {
+
+      my $weird_rs = $s->resultset('Artist')->search({}, {
+        %$col_and_join_args, %$pref_args,
+      });
+
+      throws_ok
+        { $weird_rs->$call }
+        qr/\QResult collapse not possible - selection from a has_many source redirected to the main object/
+      for (1,2);
+    }
+  }
+}
+
 done_testing;