From: Matt S Trout Date: Fri, 12 May 2006 00:56:54 +0000 (+0000) Subject: performance fix for cascade_update X-Git-Tag: v0.07002~101 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=8417f5ee39a5c27e40473255c8078d55ede78501;p=dbsrgits%2FDBIx-Class.git performance fix for cascade_update --- diff --git a/Changes b/Changes index c5716e7..cfc7ee3 100644 --- a/Changes +++ b/Changes @@ -1,6 +1,7 @@ Revision history for DBIx::Class 0.06003 + - don't unnecessarily fetch rels for cascade_update - don't set_columns explicitly in update_or_create; instead use update($hashref) so InflateColumn works - fix for has_many prefetch with 0 related rows diff --git a/lib/DBIx/Class/Relationship/CascadeActions.pm b/lib/DBIx/Class/Relationship/CascadeActions.pm index aa88043..3d5da76 100644 --- a/lib/DBIx/Class/Relationship/CascadeActions.pm +++ b/lib/DBIx/Class/Relationship/CascadeActions.pm @@ -33,6 +33,10 @@ sub update { my %rels = map { $_ => $source->relationship_info($_) } $source->relationships; my @cascade = grep { $rels{$_}{attrs}{cascade_update} } keys %rels; foreach my $rel (@cascade) { + next if ( + $rels{$rel}{attrs}{accessor} eq 'single' + && !exists($self->{_relationship_data}{$rel}) + ); $_->update for grep defined, $self->$rel; } return $ret; diff --git a/lib/DBIx/Class/ResultSet.pm b/lib/DBIx/Class/ResultSet.pm index 3efbf00..2651034 100644 --- a/lib/DBIx/Class/ResultSet.pm +++ b/lib/DBIx/Class/ResultSet.pm @@ -392,6 +392,10 @@ sub cursor { Inflates the first result without creating a cursor if the resultset has any records in it; if not returns nothing. Used by find() as an optimisation. +Can optionally take an additional condition *only* - this is a fast-code-path +method; if you need to add extra joins or similar call ->search and then +->single without a condition on the $rs returned from that. + =cut sub single { @@ -1485,6 +1489,83 @@ C can be used with the following relationship types: C, C (or if you're using C, any relationship declared with an accessor type of 'single' or 'filter'). +=head2 page + +=over 4 + +=item Value: $page + +=back + +Makes the resultset paged and specifies the page to retrieve. Effectively +identical to creating a non-pages resultset and then calling ->page($page) +on it. + +=head2 rows + +=over 4 + +=item Value: $rows + +=back + +Specifes the maximum number of rows for direct retrieval or the number of +rows per page if the page attribute or method is used. + +=head2 group_by + +=over 4 + +=item Value: \@columns + +=back + +A arrayref of columns to group by. Can include columns of joined tables. + + group_by => [qw/ column1 column2 ... /] + +=head2 having + +=over 4 + +=item Value: $condition + +=back + +HAVING is a select statement attribute that is applied between GROUP BY and +ORDER BY. It is applied to the after the grouping calculations have been +done. + + having => { 'count(employee)' => { '>=', 100 } } + +=head2 distinct + +=over 4 + +=item Value: (0 | 1) + +=back + +Set to 1 to group by all columns. + +=head2 cache + +Set to 1 to cache search results. This prevents extra SQL queries if you +revisit rows in your ResultSet: + + my $resultset = $schema->resultset('Artist')->search( undef, { cache => 1 } ); + + while( my $artist = $resultset->next ) { + ... do stuff ... + } + + $rs->first; # without cache, this would issue a query + +By default, searches are not cached. + +For more examples of using these attributes, see +L. + =head2 from =over 4 @@ -1498,21 +1579,35 @@ statements generated by L, allowing you to express custom C clauses. NOTE: Use this on your own risk. This allows you to shoot off your foot! + C will usually do what you need and it is strongly recommended that you avoid using C unless you cannot achieve the desired result using C. +And we really do mean "cannot", not just tried and failed. Attempting to use +this because you're having problems with C is like trying to use x86 +ASM because you've got a syntax error in your C. Trust us on this. + +Now, if you're still really, really sure you need to use this (and if you're +not 100% sure, ask the mailing list first), here's an explanation of how this +works. -In simple terms, C works as follows: +The syntax is as follows - + [ + { => }, [ - { => , -join_type => 'inner|left|right' } - [] # nested JOIN (optional) - { => } - ] + { => , -join_type => 'inner|left|right' }, + [], # nested JOIN (optional) + { => , ... (more conditions) }, + ], + # More of the above [ ] may follow for additional joins + ] - JOIN -
- [JOIN ...] - ON = + + JOIN + + [JOIN ...] + ON = + An easy way to follow the examples below is to remember the following: @@ -1578,83 +1673,6 @@ with a father in the person table, we could explicitly use C: # SELECT child.* FROM person child # INNER JOIN person father ON child.father_id = father.id -=head2 page - -=over 4 - -=item Value: $page - -=back - -Makes the resultset paged and specifies the page to retrieve. Effectively -identical to creating a non-pages resultset and then calling ->page($page) -on it. - -=head2 rows - -=over 4 - -=item Value: $rows - -=back - -Specifes the maximum number of rows for direct retrieval or the number of -rows per page if the page attribute or method is used. - -=head2 group_by - -=over 4 - -=item Value: \@columns - -=back - -A arrayref of columns to group by. Can include columns of joined tables. - - group_by => [qw/ column1 column2 ... /] - -=head2 having - -=over 4 - -=item Value: $condition - -=back - -HAVING is a select statement attribute that is applied between GROUP BY and -ORDER BY. It is applied to the after the grouping calculations have been -done. - - having => { 'count(employee)' => { '>=', 100 } } - -=head2 distinct - -=over 4 - -=item Value: (0 | 1) - -=back - -Set to 1 to group by all columns. - -=head2 cache - -Set to 1 to cache search results. This prevents extra SQL queries if you -revisit rows in your ResultSet: - - my $resultset = $schema->resultset('Artist')->search( undef, { cache => 1 } ); - - while( my $artist = $resultset->next ) { - ... do stuff ... - } - - $rs->first; # without cache, this would issue a query - -By default, searches are not cached. - -For more examples of using these attributes, see -L. - =cut 1; diff --git a/maint/gen-tests.pl b/maint/gen-tests.pl index 0fc6180..48e71a7 100755 --- a/maint/gen-tests.pl +++ b/maint/gen-tests.pl @@ -22,4 +22,4 @@ run_tests(DBICTest->schema); EOF close $fh; } -} \ No newline at end of file +} diff --git a/t/basicrels/26might_have.t b/t/basicrels/26might_have.t new file mode 100644 index 0000000..f2942e4 --- /dev/null +++ b/t/basicrels/26might_have.t @@ -0,0 +1,7 @@ +use Test::More; +use lib qw(t/lib); +use DBICTest; +use DBICTest::BasicRels; + +require "t/run/26might_have.tl"; +run_tests(DBICTest->schema); diff --git a/t/helperrels/26might_have.t b/t/helperrels/26might_have.t new file mode 100644 index 0000000..d3ec615 --- /dev/null +++ b/t/helperrels/26might_have.t @@ -0,0 +1,7 @@ +use Test::More; +use lib qw(t/lib); +use DBICTest; +use DBICTest::HelperRels; + +require "t/run/26might_have.tl"; +run_tests(DBICTest->schema); diff --git a/t/run/26might_have.tl b/t/run/26might_have.tl new file mode 100644 index 0000000..a1c534e --- /dev/null +++ b/t/run/26might_have.tl @@ -0,0 +1,43 @@ +sub run_tests { +my $schema = shift; + +my $queries; +#$schema->storage->debugfh(IO::File->new('t/var/temp.trace', 'w')); +$schema->storage->debugcb( sub{ $queries++ } ); + +eval "use DBD::SQLite"; +plan skip_all => 'needs DBD::SQLite for testing' if $@; +plan tests => 2; + + +my $cd = $schema->resultset("CD")->find(1); +$cd->title('test'); + +# SELECT count +$queries = 0; +$schema->storage->debug(1); + +$cd->update; + +is($queries, 1, 'liner_notes (might_have) not prefetched - do not load +liner_notes on update'); + +$schema->storage->debug(0); + + +my $cd2 = $schema->resultset("CD")->find(2, {prefetch => 'liner_notes'}); +$cd2->title('test'); + +# SELECT count +$queries = 0; +$schema->storage->debug(1); + +$cd2->update; + +is($queries, 1, 'liner_notes (might_have) prefetched - do not load +liner_notes on update'); + +$schema->storage->debug(0); +} + +1;