X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FStorage%2FDBI.pm;h=3937cb51a827624a4c00a646d178b738c74b35fe;hb=fba8d76cb4013ec7471ec4c2aff4352b5c006457;hp=8df1894bb1af055dc027c6863e44c2ed949659fb;hpb=341d5edefa34c7cdfee74be00eba9cd44ad2b7c4;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/Storage/DBI.pm b/lib/DBIx/Class/Storage/DBI.pm index 8df1894..3937cb5 100644 --- a/lib/DBIx/Class/Storage/DBI.pm +++ b/lib/DBIx/Class/Storage/DBI.pm @@ -1051,6 +1051,93 @@ sub delete { return $self->_execute('delete' => [], $source, $bind_attrs, @_); } +# We were sent here because the $rs contains a complex search +# which will require a subquery to select the correct rows +# (i.e. joined or limited resultsets) +# +# 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() +sub subq_update_delete { + my $self = shift; + my ($rs, $op, $values) = @_; + + if ($rs->result_source->primary_columns == 1) { + return $self->_onepk_update_delete (@_); + } + 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 resort to simple +# (and inefficient) delete_all style per-row opearations, while allowing +# specific storages to override this with a faster implementation. +# +# 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 { + 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; + + my $subrs_cur = $rs->cursor; + while (my @pks = $subrs_cur->next) { + + my $cond; + for my $i (0.. $#pcols) { + $cond->{$pcols[$i]} = $pks[$i]; + } + + $self->$op ( + $rsrc, + $op eq 'update' ? $values : (), + $cond, + ); + } + + $guard->commit; + + return 1; +} + + sub _select { my $self = shift; my $sql_maker = $self->sql_maker;