More refactoring, prefetch
Matt S Trout [Fri, 9 Sep 2005 07:34:34 +0000 (07:34 +0000)]
Changes
lib/DBIx/Class/ResultSet.pm
lib/DBIx/Class/Row.pm
lib/DBIx/Class/Test/SQLite.pm
t/16joins.t
t/lib/DBICTest/Schema/CD.pm

diff --git a/Changes b/Changes
index 330c8b5..fec0d81 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,5 +1,8 @@
 Revision history for DBIx::Class\r
 \r
+0.03\r
+        - Paging support\r
+\r
 0.02    2005-08-12 18:00:00\r
         - Test fixes.\r
         - Performance improvements.\r
index e8513b2..af6cf02 100644 (file)
@@ -12,10 +12,26 @@ sub new {
   #use Data::Dumper; warn Dumper(@_);
   $it_class = ref $it_class if ref $it_class;
   $attrs = { %{ $attrs || {} } };
+  my %seen;
+  $attrs->{cols} ||= [ map { "me.$_" } $db_class->_select_columns ];
+  $attrs->{from} ||= [ { 'me' => $db_class->_table_name } ];
   if ($attrs->{join}) {
-    $attrs->{from} = [ { 'me' => $db_class->_table_name },
-                         $db_class->_resolve_join($attrs->{join}, 'me') ];
-    $attrs->{cols} = [ map { "me.$_" } $db_class->_select_columns ];
+    foreach my $j (ref $attrs->{join} eq 'ARRAY'
+              ? (@{$attrs->{join}}) : ($attrs->{join})) {
+      if (ref $j eq 'HASH') {
+        $seen{$_} = 1 foreach keys %$j;
+      } else {
+        $seen{$j} = 1;
+      }
+    }
+    push(@{$attrs->{from}}, $db_class->_resolve_join($attrs->{join}, 'me'));
+  }
+  foreach my $pre (@{$attrs->{prefetch} || []}) {
+    push(@{$attrs->{from}}, $db_class->_resolve_join($pre, 'me'))
+      unless $seen{$pre};
+    push(@{$attrs->{cols}},
+      map { "$pre.$_" }
+      $db_class->_relationships->{$pre}->{class}->columns);
   }
   my $new = {
     class => $db_class,
@@ -56,7 +72,37 @@ sub next {
   my ($self) = @_;
   my @row = $self->cursor->next;
   return unless (@row);
-  return $self->{class}->_row_to_object($self->{cols}, \@row);
+  return $self->_construct_object(@row);
+}
+
+sub _construct_object {
+  my ($self, @row) = @_;
+  my @cols = $self->{class}->_select_columns;
+  unless ($self->{attrs}{prefetch}) {
+    return $self->{class}->_row_to_object(\@cols, \@row);
+  } else {
+    my @main = splice(@row, 0, scalar @cols);
+    my $new = $self->{class}->_row_to_object(\@cols, \@main);
+    PRE: foreach my $pre (@{$self->{attrs}{prefetch}}) {
+      my $rel_obj = $self->{class}->_relationships->{$pre};
+      my @pre_cols = $rel_obj->{class}->columns;
+      my @vals = splice(@row, 0, scalar @pre_cols);
+      my $fetched = $rel_obj->{class}->_row_to_object(\@pre_cols, \@vals);
+      $self->{class}->throw("No accessor for prefetched $pre")
+        unless defined $rel_obj->{attrs}{accessor};
+      if ($rel_obj->{attrs}{accessor} eq 'single') {
+        foreach my $pri ($rel_obj->{class}->primary_columns) {
+          next PRE unless defined $fetched->get_column($pri);
+        }
+        $new->{_relationship_data}{$pre} = $fetched;
+      } elsif ($rel_obj->{attrs}{accessor} eq 'filter') {
+        $new->{_inflated_column}{$pre} = $fetched;
+      } else {
+        $self->{class}->throw("Don't know to to store prefetched $pre");
+      }
+    }
+    return $new;
+  }
 }
 
 sub count {
@@ -80,7 +126,7 @@ sub count {
 
 sub all {
   my ($self) = @_;
-  return map { $self->{class}->_row_to_object($self->{cols}, $_); }
+  return map { $self->_construct_object(@$_); }
            $self->cursor->all;
 }
 
index cb086c8..f789b29 100644 (file)
@@ -211,11 +211,7 @@ sub store_column {
 sub _row_to_object {
   my ($class, $cols, $row) = @_;
   my %vals;
-  foreach my $pos (0..$#$cols) {
-    my $c = $cols->[$pos];
-    $c =~ s/^me\.//;
-    $vals{$c} = $row->[$pos];
-  }
+  $vals{$cols->[$_]} = $row->[$_] for 0 .. $#$cols;
   my $new = $class->new(\%vals);
   $new->in_storage(1);
   return $new;
index 94dd418..7494f41 100644 (file)
@@ -22,11 +22,11 @@ DBIx::Class::Test::SQLite - Base class for running Class::DBI tests against DBIx
        
 =head1 DESCRIPTION
 
-This provides a simple base class for DBIx::Class tests using SQLite.
-Each class for the test should inherit from this, provide a create_sql()
-method which returns a string representing the SQL used to create the
-table for the class, and then call set_table() to create the table, and
-tie it to the class.
+This provides a simple base class for DBIx::Class::CDBICompat tests using
+SQLite.  Each class for the test should inherit from this, provide a
+create_sql() method which returns a string representing the SQL used to
+create the table for the class, and then call set_table() to create the
+table, and tie it to the class.
 
 =cut
 
index d8475a5..a334110 100644 (file)
@@ -5,7 +5,7 @@ BEGIN {
     eval "use DBD::SQLite";
     plan $@
         ? ( skip_all => 'needs DBD::SQLite for testing' )
-        : ( tests => 12 );
+        : ( tests => 17 );
 }
 
 use lib qw(t/lib);
@@ -56,10 +56,10 @@ is( $sa->_recurse_from(@j3), $match, 'join 3 (inner join) ok');
 
 my $rs = DBICTest::CD->search(
            { 'year' => 2001, 'artist.name' => 'Caterwauler McCrae' },
-           { from => [ { 'cd' => 'cd' },
+           { from => [ { 'me' => 'cd' },
                          [
                            { artist => 'artist' },
-                           { 'cd.artist' => 'artist.artistid' }
+                           { 'me.artist' => 'artist.artistid' }
                          ] ] }
          );
 
@@ -91,3 +91,32 @@ $rs = DBICTest::Artist->search(
 cmp_ok( $rs->count, '==', 1, "Single record in resultset");
 
 is($rs->first->name, 'We Are Goth', 'Correct record returned');
+
+DBICTest::Schema::CD->add_relationship(
+    artist => 'DBICTest::Schema::Artist',
+    { 'foreign.artistid' => 'self.artist' },
+    { accessor => 'filter' },
+);
+
+DBICTest::Schema::CD->add_relationship(
+    liner_notes => 'DBICTest::Schema::LinerNotes',
+    { 'foreign.liner_id' => 'self.cdid' },
+    { join_type => 'LEFT', accessor => 'single' });
+
+
+$rs = DBICTest::CD->search(
+           { 'artist.name' => 'Caterwauler McCrae' },
+           { prefetch => [ qw/artist liner_notes/ ],
+             order_by => 'me.cdid' });
+
+cmp_ok($rs->count, '==', 3, 'Correct number of records returned');
+
+my @cd = $rs->all;
+
+is($cd[0]->title, 'Spoonful of bees', 'First record returned ok');
+
+ok(!exists $cd[0]->{_relationship_data}{liner_notes}, 'No prefetch for NULL LEFT JOIN');
+
+is($cd[1]->{_relationship_data}{liner_notes}->notes, 'Buy Whiskey!', 'Prefetch for present LEFT JOIN');
+
+is($cd[2]->{_inflated_column}{artist}->name, 'Caterwauler McCrae', 'Prefetch on parent object ok');
index d6b130a..457f8ac 100644 (file)
@@ -7,7 +7,7 @@ DBICTest::Schema::CD->add_columns(qw/cdid artist title year/);
 DBICTest::Schema::CD->set_primary_key('cdid');
 DBICTest::Schema::CD->add_relationship(
     artist => 'DBICTest::Schema::Artist',
-    { 'foreign.artistid' => 'self.artist' }
+    { 'foreign.artistid' => 'self.artist' },
 );
 DBICTest::Schema::CD->add_relationship(
     tracks => 'DBICTest::Schema::Track',