From: Matt S Trout Date: Fri, 9 Sep 2005 07:34:34 +0000 (+0000) Subject: More refactoring, prefetch X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=c7ce65e6f0d957656f8e3ca26944cd9c1f03ddbc;p=dbsrgits%2FDBIx-Class-Historic.git More refactoring, prefetch --- diff --git a/Changes b/Changes index 330c8b5..fec0d81 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,8 @@ Revision history for DBIx::Class +0.03 + - Paging support + 0.02 2005-08-12 18:00:00 - Test fixes. - Performance improvements. diff --git a/lib/DBIx/Class/ResultSet.pm b/lib/DBIx/Class/ResultSet.pm index e8513b2..af6cf02 100644 --- a/lib/DBIx/Class/ResultSet.pm +++ b/lib/DBIx/Class/ResultSet.pm @@ -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; } diff --git a/lib/DBIx/Class/Row.pm b/lib/DBIx/Class/Row.pm index cb086c8..f789b29 100644 --- a/lib/DBIx/Class/Row.pm +++ b/lib/DBIx/Class/Row.pm @@ -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; diff --git a/lib/DBIx/Class/Test/SQLite.pm b/lib/DBIx/Class/Test/SQLite.pm index 94dd418..7494f41 100644 --- a/lib/DBIx/Class/Test/SQLite.pm +++ b/lib/DBIx/Class/Test/SQLite.pm @@ -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 diff --git a/t/16joins.t b/t/16joins.t index d8475a5..a334110 100644 --- a/t/16joins.t +++ b/t/16joins.t @@ -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'); diff --git a/t/lib/DBICTest/Schema/CD.pm b/t/lib/DBICTest/Schema/CD.pm index d6b130a..457f8ac 100644 --- a/t/lib/DBICTest/Schema/CD.pm +++ b/t/lib/DBICTest/Schema/CD.pm @@ -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',