From: Peter Rabbitson Date: Sat, 23 May 2009 08:09:46 +0000 (+0000) Subject: Refactor rs_update_delete (too many methods for no reason) X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=613f65e5493254510c8201119165bcb55291b516;p=dbsrgits%2FDBIx-Class-Historic.git Refactor rs_update_delete (too many methods for no reason) Make mysql use _per_row_update_delete by default as this is the only thing it understands: http://dev.mysql.com/doc/refman/5.0/en/subquery-errors.html --- diff --git a/lib/DBIx/Class/Storage/DBI.pm b/lib/DBIx/Class/Storage/DBI.pm index dc66cb6..83de252 100644 --- a/lib/DBIx/Class/Storage/DBI.pm +++ b/lib/DBIx/Class/Storage/DBI.pm @@ -1082,63 +1082,63 @@ sub delete { # # Genarating a single PK column subquery is trivial and supported # by all RDBMS. However if we have a multicolumn PK, things get ugly. -# Look at multipk_update_delete() +# Look at _multipk_update_delete() sub subq_update_delete { my $self = shift; my ($rs, $op, $values) = @_; - if ($rs->result_source->primary_columns == 1) { - return $self->_onepk_update_delete (@_); + my $rsrc = $rs->result_source; + + # we already check this, but double check naively just in case. Should be removed soon + my $sel = $rs->_resolved_attrs->{select}; + $sel = [ $sel ] unless ref $sel eq 'ARRAY'; + my @pcols = $rsrc->primary_columns; + if (@$sel != @pcols) { + $self->throw_exception ( + 'Subquery update/delete can not be called on resultsets selecting a' + .' number of columns different than the number of primary keys' + ); + } + + if (@pcols == 1) { + return $self->$op ( + $rsrc, + $op eq 'update' ? $values : (), + { $pcols[0] => { -in => $rs->as_query } }, + ); } + else { return $self->_multipk_update_delete (@_); } } -# Generally a single PK resultset operation is trivially expressed -# with PK IN (subquery). However some databases (mysql) do not support -# modification of a table mentioned in the subselect. This method -# should be overriden in the appropriate storage class to be smarter -# in such situations -sub _onepk_update_delete { - - my $self = shift; - my ($rs, $op, $values) = @_; - - my $rsrc = $rs->result_source; - my $attrs = $rs->_resolved_attrs; - my @pcols = $rsrc->primary_columns; - - $self->throw_exception ('_onepk_update_delete can not be called on resultsets selecting multiple columns') - if (ref $attrs->{select} eq 'ARRAY' and @{$attrs->{select}} > 1); - - return $self->$op ( - $rsrc, - $op eq 'update' ? $values : (), - { $pcols[0] => { -in => $rs->as_query } }, - ); +# ANSI SQL does not provide a reliable way to perform a multicol-PK +# resultset update/delete involving subqueries. So by default resort +# to simple (and inefficient) delete_all style per-row opearations, +# while allowing specific storages to override this with a faster +# implementation. +# +sub _multipk_update_delete { + return shift->_per_row_update_delete (@_); } -# ANSI SQL does not provide a reliable way to perform a multicol-PK -# resultset update/delete involving subqueries. So resort to simple -# (and inefficient) delete_all style per-row opearations, while allowing -# specific storages to override this with a faster implementation. +# This is the default loop used to delete/update rows for multi PK +# resultsets, and used by mysql exclusively (because it can't do anything +# else). # # We do not use $row->$op style queries, because resultset update/delete # is not expected to cascade (this is what delete_all/update_all is for). # # There should be no race conditions as the entire operation is rolled # in a transaction. -sub _multipk_update_delete { +# +sub _per_row_update_delete { my $self = shift; my ($rs, $op, $values) = @_; my $rsrc = $rs->result_source; my @pcols = $rsrc->primary_columns; - my $attrs = $rs->_resolved_attrs; - - $self->throw_exception ('Number of columns selected by supplied resultset does not match number of primary keys') - if ( ref $attrs->{select} ne 'ARRAY' or @{$attrs->{select}} != @pcols ); my $guard = $self->txn_scope_guard; @@ -1162,7 +1162,6 @@ sub _multipk_update_delete { return 1; } - sub _select { my $self = shift; my $sql_maker = $self->sql_maker; diff --git a/lib/DBIx/Class/Storage/DBI/mysql.pm b/lib/DBIx/Class/Storage/DBI/mysql.pm index fa0419f..2cb4be9 100644 --- a/lib/DBIx/Class/Storage/DBI/mysql.pm +++ b/lib/DBIx/Class/Storage/DBI/mysql.pm @@ -51,6 +51,12 @@ sub lag_behind_master { return shift->dbh->selectrow_hashref('show slave status')->{Seconds_Behind_Master}; } +# MySql can not do subquery update/deletes, only way is slow per-row operations. +# This assumes you have proper privilege separation and use innodb. +sub subq_update_delete { + return shift->_per_row_update_delete (@_); +} + 1; =head1 NAME