From c2362461cdf9615d5704d6f2942921b84b854c3c Mon Sep 17 00:00:00 2001 From: Johnny Holton Date: Sat, 18 May 2013 18:16:38 -0400 Subject: destroys association records before saving/inserting new association records This is a backport of #10417 fixes bug introduced by #3329 These are the conditions necessary to reproduce the bug: - For an association, autosave => true. - An association record is being destroyed - A new association record is being created. - There is a unique index one of the association's fields. - The record being created has the same value as the record being destroyed on the indexed field. Before, the deletion of records was postponed until after all insertions/saves. Therefore the new record with the identical value in the indexed field caused a non-unique value error to be thrown at the database level. With this fix, the deletions happen first, before the insertions/saves. Therefore the record with the duplicate value is gone from the database before the new record is created, thereby avoiding the non-uniuqe value error. --- .../test/cases/autosave_association_test.rb | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) (limited to 'activerecord/test/cases/autosave_association_test.rb') diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index e6b881389b..bb07cf4185 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -583,7 +583,7 @@ class TestDefaultAutosaveAssociationOnNewRecord < ActiveRecord::TestCase end class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase - self.use_transactional_fixtures = false unless supports_savepoints? + self.use_transactional_fixtures = false def setup @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?") @@ -780,6 +780,20 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase assert_equal 2, @pirate.birds.reload.length end + def test_should_save_new_record_that_has_same_value_as_existing_record_marked_for_destruction_on_field_that_has_unique_index + Bird.connection.add_index :birds, :name, unique: true + + 3.times { |i| @pirate.birds.create(name: "unique_birds_#{i}") } + + @pirate.birds[0].mark_for_destruction + @pirate.birds.build(name: @pirate.birds[0].name) + @pirate.save! + + assert_equal 3, @pirate.birds.reload.length + ensure + Bird.connection.remove_index :birds, column: :name + end + # Add and remove callbacks tests for association collections. %w{ method proc }.each do |callback_type| define_method("test_should_run_add_callback_#{callback_type}s_for_has_many") do @@ -862,8 +876,10 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase @pirate.parrots.each { |parrot| parrot.mark_for_destruction } assert @pirate.save - assert_queries(0) do - assert @pirate.save + Pirate.transaction do + assert_queries(0) do + assert @pirate.save + end end end -- cgit v1.2.3