Fix grave resultset-level delete/update bug
Peter Rabbitson [Wed, 4 Jun 2014 23:44:37 +0000 (01:44 +0200)]
In case of a read-modify cycle (usually due to multicolumn PK, without
multicolumn IN support) DBIC would end up with a null condition and nuke
the entire underlying table from orbit

I have no words towards my younger self...

Changes
lib/DBIx/Class/ResultSet.pm
t/resultset/update_delete.t

diff --git a/Changes b/Changes
index 2932f71..c82b355 100644 (file)
--- a/Changes
+++ b/Changes
@@ -7,6 +7,8 @@ Revision history for DBIx::Class
           returned from storage
 
     * Fixes
+        - Fix Resultset delete/update affecting *THE ENTIRE TABLE* in cases
+          of empty (due to conditions) resultsets with multi-column keys
         - Fix on_connect_* not always firing in some cases - a race condition
           existed between storage accessor setters and the determine_driver
           routines, triggering a connection before the set-cycle is finished
index e2c87dd..ca8a23e 100644 (file)
@@ -1944,7 +1944,6 @@ sub _rs_update_delete {
 
       $guard = $storage->txn_scope_guard;
 
-      $cond = [];
       for my $row ($subrs->cursor->all) {
         push @$cond, { map
           { $idcols->[$_] => $row->[$_] }
@@ -1954,11 +1953,11 @@ sub _rs_update_delete {
     }
   }
 
-  my $res = $storage->$op (
+  my $res = $cond ? $storage->$op (
     $rsrc,
     $op eq 'update' ? $values : (),
     $cond,
-  );
+  ) : '0E0';
 
   $guard->commit if $guard;
 
index ee32717..c3e8c2f 100644 (file)
@@ -105,6 +105,29 @@ is ($fa->discard_changes->read_count, 12, 'Update ran only once on joined result
 is ($fb->discard_changes->read_count, 22, 'Update ran only once on joined resultset');
 is ($fc->discard_changes->read_count, 30, 'Update did not touch outlier');
 
+$schema->is_executed_sql_bind( sub {
+  my $res = $fks_multi->search (\' "blah" = "bleh" ')->delete;
+  ok ($res, 'operation is true');
+  cmp_ok ($res, '==', 0, 'zero rows affected');
+}, [
+  [ 'BEGIN' ],
+  [
+    'SELECT me.foo, me.bar, me.hello, me.goodbye
+      FROM fourkeys me
+      LEFT JOIN fourkeys_to_twokeys fourkeys_to_twokeys
+        ON fourkeys_to_twokeys.f_bar = me.bar AND fourkeys_to_twokeys.f_foo = me.foo AND fourkeys_to_twokeys.f_goodbye = me.goodbye AND fourkeys_to_twokeys.f_hello = me.hello
+      WHERE "blah" = "bleh" AND ( bar = ? OR bar = ? ) AND ( foo = ? OR foo = ? ) AND fourkeys_to_twokeys.pilot_sequence != ? AND ( goodbye = ? OR goodbye = ? ) AND ( hello = ? OR hello = ? ) AND sensors != ?
+      GROUP BY me.foo, me.bar, me.hello, me.goodbye
+    ',
+    (1, 2) x 2,
+    666,
+    (1, 2) x 2,
+    'c',
+  ],
+  [ 'COMMIT' ],
+], 'Correct null-delete-SQL with multijoin without pruning' );
+
+
 # try the same sql with forced multicolumn in
 $schema->is_executed_sql_bind( sub {
   local $schema->storage->{_use_multicolumn_in} = 1;