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

Using subselect for delete_all with limit or offset

Arel doesn't support subselect generation for DELETE unlike UPDATE yet,
but we already have that generation in connection adapters. We can
simply use the subselect generated by that one.
This commit is contained in:
Ryuta Kamizono 2017-12-19 20:10:51 +09:00
parent 1118e2c289
commit 9e7260da1b
4 changed files with 29 additions and 6 deletions

View file

@ -1,3 +1,7 @@
* Using subselect for `delete_all` with `limit` or `offset`.
*Ryuta Kamizono*
* Undefine attribute methods on descendants when resetting column
information.
@ -553,4 +557,5 @@
*Kevin McPhillips*
Please check [5-1-stable](https://github.com/rails/rails/blob/5-1-stable/activerecord/CHANGELOG.md) for previous changes.

View file

@ -10,7 +10,7 @@ module ActiveRecord
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering,
:reverse_order, :distinct, :create_with, :skip_query_cache]
CLAUSE_METHODS = [:where, :having, :from]
INVALID_METHODS_FOR_DELETE_ALL = [:limit, :distinct, :offset, :group, :having]
INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :group, :having]
VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS + CLAUSE_METHODS
@ -365,8 +365,8 @@ module ActiveRecord
#
# If an invalid method is supplied, #delete_all raises an ActiveRecordError:
#
# Post.limit(100).delete_all
# # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit
# Post.distinct.delete_all
# # => ActiveRecord::ActiveRecordError: delete_all doesn't support distinct
def delete_all
invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select do |method|
value = get_value(method)
@ -384,7 +384,7 @@ module ActiveRecord
stmt = Arel::DeleteManager.new
stmt.from(table)
if has_join_values?
if has_join_values? || has_limit_or_offset?
@klass.connection.join_to_delete(stmt, arel, arel_attribute(primary_key))
else
stmt.wheres = arel.constraints

View file

@ -76,6 +76,26 @@ class PersistenceTest < ActiveRecord::TestCase
assert_equal "bulk update!", posts(:thinking).body
assert_not_equal "bulk update!", posts(:welcome).body
end
def test_delete_all_with_order_and_limit_deletes_subset_only
author = authors(:david)
limited_posts = Post.where(author: author).order(:id).limit(1)
assert_equal 1, limited_posts.size
assert_equal 2, limited_posts.limit(2).size
assert_equal 1, limited_posts.delete_all
assert_raise(ActiveRecord::RecordNotFound) { posts(:welcome) }
assert posts(:thinking)
end
def test_delete_all_with_order_and_limit_and_offset_deletes_subset_only
author = authors(:david)
limited_posts = Post.where(author: author).order(:id).limit(1).offset(1)
assert_equal 1, limited_posts.size
assert_equal 2, limited_posts.limit(2).size
assert_equal 1, limited_posts.delete_all
assert_raise(ActiveRecord::RecordNotFound) { posts(:thinking) }
assert posts(:welcome)
end
end
def test_update_many

View file

@ -896,11 +896,9 @@ class RelationTest < ActiveRecord::TestCase
end
def test_delete_all_with_unpermitted_relation_raises_error
assert_raises(ActiveRecord::ActiveRecordError) { Author.limit(10).delete_all }
assert_raises(ActiveRecord::ActiveRecordError) { Author.distinct.delete_all }
assert_raises(ActiveRecord::ActiveRecordError) { Author.group(:name).delete_all }
assert_raises(ActiveRecord::ActiveRecordError) { Author.having("SUM(id) < 3").delete_all }
assert_raises(ActiveRecord::ActiveRecordError) { Author.offset(10).delete_all }
end
def test_select_with_aggregates