1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Return rows affected from batched update_all and delete_all

The previous return value of nil was undocumented and inconsistent with
the non-batched versions of these methods.

Also lean on `each` to create the batches, and add API documentation for
`update_all`, `delete_all`, and `destroy_all` on `BatchEnumerator`.
This commit is contained in:
Eugene Kenny 2020-09-26 18:26:02 +01:00
parent 166b63eb67
commit 80e73ad7c1
3 changed files with 53 additions and 9 deletions

View file

@ -1,3 +1,15 @@
* `BatchEnumerator#update_all` and `BatchEnumerator#delete_all` now return the
total number of rows affected, just like their non-batched counterparts.
```ruby
Person.in_batches.update_all("first_name = 'Eugene'") # => 42
Person.in_batches.delete_all # => 42
```
Fixes #40287.
*Eugene Kenny*
* Add support for PostgreSQL `interval` data type with conversion to * Add support for PostgreSQL `interval` data type with conversion to
`ActiveSupport::Duration` when loading records from database and `ActiveSupport::Duration` when loading records from database and
serialization to ISO 8601 formatted duration string on save. serialization to ISO 8601 formatted duration string on save.

View file

@ -41,19 +41,35 @@ module ActiveRecord
end end
end end
# Delegates #delete_all, #update_all, #destroy_all methods to each batch. # Deletes records in batches. Returns the total number of rows affected.
# #
# People.in_batches.delete_all # Person.in_batches.delete_all
# People.where('age < 10').in_batches.destroy_all #
# People.in_batches.update_all('age = age + 1') # See Relation#delete_all for details of how each batch is deleted.
[:delete_all, :update_all, :destroy_all].each do |method| def delete_all
define_method(method) do |*args, &block| sum(&:delete_all)
@relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false).each do |relation| end
relation.public_send(method, *args, &block)
end # Updates records in batches. Returns the total number of rows affected.
#
# Person.in_batches.update_all("age = age + 1")
#
# See Relation#update_all for details of how each batch is updated.
def update_all(updates)
sum do |relation|
relation.update_all(updates)
end end
end end
# Destroys records in batches.
#
# Person.where("age < 10").in_batches.destroy_all
#
# See Relation#destroy_all for details of how each batch is destroyed.
def destroy_all
each(&:destroy_all)
end
# Yields an ActiveRecord::Relation object for each batch of records. # Yields an ActiveRecord::Relation object for each batch of records.
# #
# Person.in_batches.each do |relation| # Person.in_batches.each do |relation|

View file

@ -356,6 +356,14 @@ class EachTest < ActiveRecord::TestCase
assert_equal Post.all.pluck(:title), ["updated-title"] * Post.count assert_equal Post.all.pluck(:title), ["updated-title"] * Post.count
end end
def test_in_batches_update_all_returns_rows_affected
assert_equal 11, Post.in_batches(of: 2).update_all(title: "updated-title")
end
def test_in_batches_update_all_returns_zero_when_no_batches
assert_equal 0, Post.where("1=0").in_batches(of: 2).update_all(title: "updated-title")
end
def test_in_batches_delete_all_should_not_delete_records_in_other_batches def test_in_batches_delete_all_should_not_delete_records_in_other_batches
not_deleted_count = Post.where("id <= 2").count not_deleted_count = Post.where("id <= 2").count
Post.where("id > 2").in_batches(of: 2).delete_all Post.where("id > 2").in_batches(of: 2).delete_all
@ -363,6 +371,14 @@ class EachTest < ActiveRecord::TestCase
assert_equal not_deleted_count, Post.count assert_equal not_deleted_count, Post.count
end end
def test_in_batches_delete_all_returns_rows_affected
assert_equal 11, Post.in_batches(of: 2).delete_all
end
def test_in_batches_delete_all_returns_zero_when_no_batches
assert_equal 0, Post.where("1=0").in_batches(of: 2).delete_all
end
def test_in_batches_should_not_be_loaded def test_in_batches_should_not_be_loaded
Post.in_batches(of: 1) do |relation| Post.in_batches(of: 1) do |relation|
assert_not_predicate relation, :loaded? assert_not_predicate relation, :loaded?