obscure prefetch problem fixed
Luke Saunders [Wed, 7 Jun 2006 23:31:44 +0000 (23:31 +0000)]
lib/DBIx/Class/ResultSet.pm
t/90join_torture.t
t/lib/DBICTest/Schema/CD.pm
t/lib/DBICTest/Schema/CD_to_Producer.pm
t/lib/DBICTest/Schema/Producer.pm

index df1f51c..51bee33 100644 (file)
@@ -754,13 +754,46 @@ sub _resolve {
              push(@{$attrs->{from}}, $source->resolve_join($p, $attrs->{alias}))
                  unless $seen{$p};
          }
-          #print "res pre: " . Dumper($p, $collapse);
-         my @prefetch = $source->resolve_prefetch(
+
+               # we're about to resolve_join on the current class, so we need to bring
+               # the joins (which are from the original class) to the right level
+               # XXX the below alg is ridiculous
+               if ($attrs->{_live_join_stack}) {
+                       STACK: foreach (@{$attrs->{_live_join_stack}}) {
+                               if (ref $p eq 'HASH') {
+                                       if (exists $p->{$_}) {
+                                               $p = $p->{$_};
+                                       } else {
+                                               $p = undef;
+                                               last STACK;
+                                       }
+                               } elsif (ref $p eq 'ARRAY') {
+                                       foreach my $pe (@{$p}) {
+                                               if ($pe eq $_) {
+                                                       $p = undef;
+                                                       last STACK;
+                                               }
+                                               next unless(ref $pe eq 'HASH');
+                                               next unless(exists $pe->{$_});
+                                               $p = $pe->{$_};
+                                               next STACK;
+                                       }                                               
+                                       $p = undef;
+                                       last STACK;
+                               } else {
+                                       $p = undef;
+                                       last STACK;
+                               }
+                       }
+               }
+               
+               if ($p) {
+                       my @prefetch = $self->result_source->resolve_prefetch(
                                                   $p, $attrs->{alias}, {}, \@pre_order, $collapse);
-            
-          #print "prefetch: " . Dumper(\@prefetch);
-         push(@{$attrs->{select}}, map { $_->[0] } @prefetch);
-         push(@{$attrs->{as}}, map { $_->[1] } @prefetch);
+               
+                       push(@{$attrs->{select}}, map { $_->[0] } @prefetch);
+                       push(@{$attrs->{as}}, map { $_->[1] } @prefetch);
+               }
       }
       push(@{$attrs->{order_by}}, @pre_order);
   }
@@ -831,7 +864,6 @@ sub _construct_object {
   my ($self, @row) = @_;
   my @as = @{ $self->{_attrs}{as} };
   
-  #print "row in: " . Dumper(\@row);
   my $info = $self->_collapse_result(\@as, \@row);
   my $new = $self->result_class->inflate_result($self->result_source, @$info);
   $new = $self->{_attrs}{record_filter}->($new)
@@ -875,7 +907,6 @@ sub _collapse_result {
   }
   my @collapse;
 
-  #print "collapse: " . Dumper($self->{_attrs}->{collapse});
   if (defined $prefix) {
     @collapse = map {
         m/^\Q${prefix}.\E(.+)$/ ? ($1) : ()
@@ -896,7 +927,6 @@ sub _collapse_result {
     my %co_check = map { ($_, $tree->[0]->{$_}); } @co_key;
     my (@final, @raw);
 
-    #print "les free: " . Dumper($tree->[0], \%co_check, \@co_key);
     while ( !(grep {
                 !defined($tree->[0]->{$_}) ||
                 $co_check{$_} ne $tree->[0]->{$_}
@@ -910,13 +940,6 @@ sub _collapse_result {
       # single empty result to indicate an empty prefetched has_many
   }
 
-  # get prefetch tree back to result_source level
-  # $self could be a related resultset
-  #if ($self->{attrs}->{_live_join_stack}) {
-   # foreach (@{$self->{attrs}->{_live_join_stack}}) {
-    #  $info->[1] = $info->[1]->{$_}->[1] if(exists $info->[1]->{$_});
-    #}
-  #}
   #print "final info: " . Dumper($info);
   return $info;
 }
@@ -1586,22 +1609,25 @@ sub related_resultset {
   return $self->{related_resultsets}{$rel} ||= do {
     #warn "fetching related resultset for rel '$rel' " . $self->result_source->{name};
     my $rel_obj = $self->result_source->relationship_info($rel);
+               #print Dumper($self->result_source->_relationships);
     $self->throw_exception(
         "search_related: result source '" . $self->result_source->name .
         "' has no such relationship ${rel}")
         unless $rel_obj; #die Dumper $self->{attrs};
 
-               my $live_join_stack = $self->{attrs}->{_live_join_stack} || [];         
-               push(@{$live_join_stack}, $rel);
+               my @live_join_stack = (exists $self->{attrs}->{_live_join_stack}) ?
+                       @{$self->{attrs}->{_live_join_stack}}:
+                       ();             
+               push(@live_join_stack, $rel);
                
     my $rs = $self->result_source->schema->resultset($rel_obj->{class}
            )->search( undef,
                       { select => undef,
                         as => undef,
                         _live_join => $rel, #the most recent
-                        _live_join_stack => $live_join_stack, #the trail of rels
+                        _live_join_stack => \@live_join_stack, #the trail of rels
                         _parent_attrs => $self->{attrs}}
-                      );    
+                       );    
 
     # keep reference of the original resultset
     $rs->{_parent_rs} = ($self->{_parent_rs}) ? $self->{_parent_rs} : $self->result_source;
index eee1e21..daf1c64 100644 (file)
@@ -7,7 +7,7 @@ use DBICTest;
 
 my $schema = DBICTest->init_schema();
 
-plan tests => 11;
+plan tests => 14;
 
 my @rs1a_results = $schema->resultset("Artist")->search_related('cds', {title => 'Forkful of bees'}, {order_by => 'title'});
 is($rs1a_results[0]->title, 'Forkful of bees', "bare field conditions okay after search related");
@@ -21,7 +21,7 @@ my @artists2 = $rs2->search({ 'producer.name' => 'Matt S Trout' });
 my @cds = $artists2[0]->cds;
 cmp_ok(scalar @cds, '==', 1, "condition based on inherited join okay");
 
-# this is wrong, should accept me.title really
+#this is wrong, should accept me.title really
 my $rs3 = $rs2->search_related('cds');
 cmp_ok($rs3->count, '==', 9, "Nine artists returned");
 
@@ -45,31 +45,31 @@ my $cd = $cds->search({'me.title' => 'Forkful of bees'}, { prefetch => 'tracks'
 my @tracks = $cd->tracks->all;
 is(scalar(@tracks), 3, 'right number of prefetched tracks after has many');
 
-# causes ambig col error due to order_by
+#causes ambig col error due to order_by
 #my $tracks_rs = $cds->search_related('tracks', { 'tracks.position' => '2', 'disc.title' => 'Forkful of bees' });
 #my $first_tracks_rs = $tracks_rs->first;
 
 my $related_rs = $schema->resultset("Artist")->search({ name => 'Caterwauler McCrae' })->search_related('cds', { year => '2001'})->search_related('tracks', { 'position' => '2' });
 is($related_rs->first->trackid, '5', 'search related on search related okay');
 
-# causes ambig col error due to order_by
+#causes ambig col error due to order_by
 #$related_rs->search({'cd.year' => '2001'}, {join => ['cd', 'cd']})->all;
 
 my $title = $schema->resultset("Artist")->search_related('twokeys')->search_related('cd')->search({'tracks.position' => '2'}, {join => 'tracks', order_by => 'tracks.trackid'})->next->title;
 is($title, 'Forkful of bees', 'search relateds with order by okay');
 
-# my $prod_rs = $schema->resultset("CD")->find(1)->other_producers;
-# my $prod_rs2 = $prod_rs->search({ name => 'Matt S Trout' }, { prefetch => 'producer_to_cd', order_by => 'other_producer.name' });
-# my $prod_first = $prod_rs2->first;
-# warn $prod_first->name;
+my $prod_rs = $schema->resultset("CD")->find(1)->producers_sorted;
+my $prod_rs2 = $prod_rs->search({ name => 'Matt S Trout' });
+my $prod_first = $prod_rs2->first;
+is($prod_first->id, '1', 'somewhat pointless search on rel with order_by on it okay');
 
 my $prod_map_rs = $schema->resultset("Artist")->find(1)->cds->search_related('cd_to_producer', {}, { join => 'producer', prefetch => 'producer' });
-use Data::Dumper;
-warn $prod_map_rs->count;
-print Dumper($prod_map_rs->{attrs});
-print Dumper($prod_map_rs->{_attrs});
-$prod_map_rs->first;
+is($prod_map_rs->next->producer->producerid, '1', 'search related with prefetch okay');
 
+my $stupid = $schema->resultset("Artist")->search_related('artist_undirected_maps', {}, { prefetch => 'artist1' })->search_related('mapped_artists')->search_related('cds', {'cds.cdid' => '2'}, { prefetch => 'tracks' });
+#use Data::Dumper; warn Dumper($stupid->{attrs});
 
+my $cd_final = $schema->resultset("Artist")->search_related('artist_undirected_maps', {}, { prefetch => 'artist1' })->search_related('mapped_artists')->search_related('cds', {'cds.cdid' => '2'}, { prefetch => 'tracks' })->first;
+is($cd_final->cdid, '2', 'bonkers search_related-with-join-midway okay');
 
 1;
index d3ad8e8..7ba727c 100644 (file)
@@ -40,7 +40,6 @@ __PACKAGE__->might_have(
     { proxy => [ qw/notes/ ] },
 );
 __PACKAGE__->many_to_many( producers => cd_to_producer => 'producer' );
-__PACKAGE__->many_to_many( other_producers => cd_to_producer => 'other_producer' );
 __PACKAGE__->many_to_many(
     producers_sorted => cd_to_producer => 'producer',
     { order_by => 'producer.name' },
index b3a2b18..117a590 100644 (file)
@@ -20,9 +20,4 @@ __PACKAGE__->belongs_to(
   { 'foreign.producerid' => 'self.producer' }
 );
 
-__PACKAGE__->belongs_to(
-  'other_producer', 'DBICTest::Schema::Producer',
-  { 'foreign.producerid' => 'self.producer' }
-);
-
 1;
index 4deee65..036f9f2 100644 (file)
@@ -17,9 +17,4 @@ __PACKAGE__->add_columns(
 __PACKAGE__->set_primary_key('producerid');
 __PACKAGE__->add_unique_constraint(prod_name => [ qw/name/ ]);
 
-__PACKAGE__->has_many(
-    producer_to_cd => 'DBICTest::Schema::CD_to_Producer' => 'producer'
-);
-
-
 1;