Skip to content
This repository
Browse code

Support updates with joins. Fixes #522.

  • Loading branch information...
commit 128d006242dae07edc65ad03e0e045adac0bbbf3 1 parent 9482554
Jon Leighton authored
9  activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -306,6 +306,15 @@ def sanitize_limit(limit)
306 306
         end
307 307
       end
308 308
 
  309
+      # The default strategy for an UPDATE with joins is to use a subquery. This doesn't work
  310
+      # on mysql (even when aliasing the tables), but mysql allows using JOIN directly in
  311
+      # an UPDATE statement, so in the mysql adapters we redefine this to do that.
  312
+      def join_to_update(update, select) #:nodoc:
  313
+        subselect = select.clone
  314
+        subselect.ast.cores.last.projections = [update.ast.key]
  315
+        update.wheres = [update.ast.key.in(subselect)]
  316
+      end
  317
+
309 318
       protected
310 319
         # Returns an array of record hashes with the column names as keys and
311 320
         # column values as values.
4  activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -577,6 +577,10 @@ def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
577 577
         where_sql
578 578
       end
579 579
 
  580
+      def join_to_update(update, select) #:nodoc:
  581
+        update.table select.ast.cores.last.source
  582
+      end
  583
+
580 584
       protected
581 585
         def quoted_columns_for_index(column_names, options = {})
582 586
           length = options[:length] if options.is_a?(Hash)
4  activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -491,6 +491,10 @@ def release_savepoint
491 491
         execute("RELEASE SAVEPOINT #{current_savepoint_name}")
492 492
       end
493 493
 
  494
+      def join_to_update(update, select) #:nodoc:
  495
+        update.table select.ast.cores.last.source
  496
+      end
  497
+
494 498
       # SCHEMA STATEMENTS ========================================
495 499
 
496 500
       def structure_dump #:nodoc:
13  activerecord/lib/active_record/relation.rb
@@ -217,13 +217,18 @@ def update_all(updates, conditions = nil, options = {})
217 217
         where(conditions).apply_finder_options(options.slice(:limit, :order)).update_all(updates)
218 218
       else
219 219
         stmt = arel.compile_update(Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates)))
  220
+        stmt.key = table[primary_key]
220 221
 
221  
-        if limit = arel.limit
222  
-          stmt.take limit
  222
+        if joins_values.any?
  223
+          @klass.connection.join_to_update(stmt, arel)
  224
+        else
  225
+          if limit = arel.limit
  226
+            stmt.take limit
  227
+          end
  228
+
  229
+          stmt.order(*arel.orders)
223 230
         end
224 231
 
225  
-        stmt.order(*arel.orders)
226  
-        stmt.key = table[primary_key]
227 232
         @klass.connection.update stmt, 'SQL', bind_values
228 233
       end
229 234
     end
8  activerecord/test/cases/relations_test.rb
@@ -965,4 +965,12 @@ def test_eager_loading_with_conditions_on_joins
965 965
   def test_ordering_with_extra_spaces
966 966
     assert_equal authors(:david), Author.order('id DESC , name DESC').last
967 967
   end
  968
+
  969
+  def test_update_all_with_joins
  970
+    comments = Comment.joins(:post).where('posts.id' => posts(:welcome).id)
  971
+    count    = comments.count
  972
+
  973
+    assert_equal count, comments.update_all(:post_id => posts(:thinking).id)
  974
+    assert_equal posts(:thinking), comments(:greetings).post
  975
+  end
968 976
 end

0 notes on commit 128d006

Please sign in to comment.
Something went wrong with that request. Please try again.