Skip to content

Commit 9e7260d

Browse files
committed
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.
1 parent 1118e2c commit 9e7260d

File tree

4 files changed

+29
-6
lines changed

4 files changed

+29
-6
lines changed

activerecord/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
* Using subselect for `delete_all` with `limit` or `offset`.
2+
3+
*Ryuta Kamizono*
4+
15
* Undefine attribute methods on descendants when resetting column
26
information.
37

@@ -553,4 +557,5 @@
553557
554558
*Kevin McPhillips*
555559
560+
556561
Please check [5-1-stable](https://github.com/rails/rails/blob/5-1-stable/activerecord/CHANGELOG.md) for previous changes.

activerecord/lib/active_record/relation.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class Relation
1010
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering,
1111
:reverse_order, :distinct, :create_with, :skip_query_cache]
1212
CLAUSE_METHODS = [:where, :having, :from]
13-
INVALID_METHODS_FOR_DELETE_ALL = [:limit, :distinct, :offset, :group, :having]
13+
INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :group, :having]
1414

1515
VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS + CLAUSE_METHODS
1616

@@ -365,8 +365,8 @@ def destroy_all
365365
#
366366
# If an invalid method is supplied, #delete_all raises an ActiveRecordError:
367367
#
368-
# Post.limit(100).delete_all
369-
# # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit
368+
# Post.distinct.delete_all
369+
# # => ActiveRecord::ActiveRecordError: delete_all doesn't support distinct
370370
def delete_all
371371
invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select do |method|
372372
value = get_value(method)
@@ -384,7 +384,7 @@ def delete_all
384384
stmt = Arel::DeleteManager.new
385385
stmt.from(table)
386386

387-
if has_join_values?
387+
if has_join_values? || has_limit_or_offset?
388388
@klass.connection.join_to_delete(stmt, arel, arel_attribute(primary_key))
389389
else
390390
stmt.wheres = arel.constraints

activerecord/test/cases/persistence_test.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,26 @@ def test_update_all_with_order_and_limit_and_offset_updates_subset_only
7676
assert_equal "bulk update!", posts(:thinking).body
7777
assert_not_equal "bulk update!", posts(:welcome).body
7878
end
79+
80+
def test_delete_all_with_order_and_limit_deletes_subset_only
81+
author = authors(:david)
82+
limited_posts = Post.where(author: author).order(:id).limit(1)
83+
assert_equal 1, limited_posts.size
84+
assert_equal 2, limited_posts.limit(2).size
85+
assert_equal 1, limited_posts.delete_all
86+
assert_raise(ActiveRecord::RecordNotFound) { posts(:welcome) }
87+
assert posts(:thinking)
88+
end
89+
90+
def test_delete_all_with_order_and_limit_and_offset_deletes_subset_only
91+
author = authors(:david)
92+
limited_posts = Post.where(author: author).order(:id).limit(1).offset(1)
93+
assert_equal 1, limited_posts.size
94+
assert_equal 2, limited_posts.limit(2).size
95+
assert_equal 1, limited_posts.delete_all
96+
assert_raise(ActiveRecord::RecordNotFound) { posts(:thinking) }
97+
assert posts(:welcome)
98+
end
7999
end
80100

81101
def test_update_many

activerecord/test/cases/relations_test.rb

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -896,11 +896,9 @@ def test_delete_all_loaded
896896
end
897897

898898
def test_delete_all_with_unpermitted_relation_raises_error
899-
assert_raises(ActiveRecord::ActiveRecordError) { Author.limit(10).delete_all }
900899
assert_raises(ActiveRecord::ActiveRecordError) { Author.distinct.delete_all }
901900
assert_raises(ActiveRecord::ActiveRecordError) { Author.group(:name).delete_all }
902901
assert_raises(ActiveRecord::ActiveRecordError) { Author.having("SUM(id) < 3").delete_all }
903-
assert_raises(ActiveRecord::ActiveRecordError) { Author.offset(10).delete_all }
904902
end
905903

906904
def test_select_with_aggregates

0 commit comments

Comments
 (0)