Skip to content

Commit b98d1e2

Browse files
committed
Perf: Don't load the association for #delete_all.
Bug rails#6289 Conflicts: activerecord/test/cases/associations/has_many_associations_test.rb
1 parent 2802ad0 commit b98d1e2

File tree

6 files changed

+48
-9
lines changed

6 files changed

+48
-9
lines changed

activerecord/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
## Rails 3.2.4 (unreleased) ##
22

3+
* Perf fix: Don't load the records when doing assoc.delete_all.
4+
GH #6289. *Jon Leighton*
5+
36
* Association preloading shouldn't be affected by the current scoping.
47
This could cause infinite recursion and potentially other problems.
58
See GH #5667. *Jon Leighton*

activerecord/lib/active_record/associations/collection_association.rb

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ def transaction(*args)
154154
#
155155
# See delete for more info.
156156
def delete_all
157-
delete(load_target).tap do
157+
delete(:all).tap do
158158
reset
159159
loaded!
160160
end
@@ -226,7 +226,17 @@ def count(column_name = nil, count_options = {})
226226
# are actually removed from the database, that depends precisely on
227227
# +delete_records+. They are in any case removed from the collection.
228228
def delete(*records)
229-
delete_or_destroy(records, options[:dependent])
229+
dependent = options[:dependent]
230+
231+
if records.first == :all
232+
if loaded? || dependent == :destroy
233+
delete_or_destroy(load_target, dependent)
234+
else
235+
delete_records(:all, dependent)
236+
end
237+
else
238+
delete_or_destroy(records, dependent)
239+
end
230240
end
231241

232242
# Destroy +records+ and remove them from this association calling

activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,17 @@ def delete_records(records, method)
4646
if sql = options[:delete_sql]
4747
records.each { |record| owner.connection.delete(interpolate(sql, record)) }
4848
else
49-
relation = join_table
50-
stmt = relation.where(relation[reflection.foreign_key].eq(owner.id).
51-
and(relation[reflection.association_foreign_key].in(records.map { |x| x.id }.compact))
52-
).compile_delete
53-
owner.connection.delete stmt
49+
relation = join_table
50+
condition = relation[reflection.foreign_key].eq(owner.id)
51+
52+
unless records == :all
53+
condition = condition.and(
54+
relation[reflection.association_foreign_key]
55+
.in(records.map { |x| x.id }.compact)
56+
)
57+
end
58+
59+
owner.connection.delete(relation.where(condition).compile_delete)
5460
end
5561
end
5662

activerecord/lib/active_record/associations/has_many_association.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,12 @@ def delete_records(records, method)
8989
records.each { |r| r.destroy }
9090
update_counter(-records.length) unless inverse_updates_counter_cache?
9191
else
92-
keys = records.map { |r| r[reflection.association_primary_key] }
93-
scope = scoped.where(reflection.association_primary_key => keys)
92+
if records == :all
93+
scope = scoped
94+
else
95+
keys = records.map { |r| r[reflection.association_primary_key] }
96+
scope = scoped.where(reflection.association_primary_key => keys)
97+
end
9498

9599
if method == :delete_all
96100
update_counter(-scope.delete_all)

activerecord/lib/active_record/associations/has_many_through_association.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ def update_through_counter?(method)
126126
def delete_records(records, method)
127127
ensure_not_nested
128128

129+
# This is unoptimised; it will load all the target records
130+
# even when we just want to delete everything.
131+
records = load_target if records == :all
132+
129133
scope = through_association.scoped.where(construct_join_attributes(*records))
130134

131135
case method

activerecord/test/cases/associations/has_many_associations_test.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1698,4 +1698,16 @@ def test_replace_returns_new_target
16981698
assert_equal [bulb1, bulb3], car.bulbs
16991699
assert_equal [bulb1, bulb3], result
17001700
end
1701+
1702+
test "delete_all, when not loaded, doesn't load the records" do
1703+
post = posts(:welcome)
1704+
1705+
assert post.taggings_with_delete_all.count > 0
1706+
assert !post.taggings_with_delete_all.loaded?
1707+
1708+
# 2 queries: one DELETE and another to update the counter cache
1709+
assert_queries(2) do
1710+
post.taggings_with_delete_all.delete_all
1711+
end
1712+
end
17011713
end

0 commit comments

Comments
 (0)