aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorGeorge Claghorn <george@basecamp.com>2018-07-30 08:40:48 -0400
committerGeorge Claghorn <george@basecamp.com>2018-07-30 09:12:55 -0400
commit59c3ed1b3f90db197000e3867f255781a02412e7 (patch)
tree7df79eea4f986183f78468c818eac1cbb3c2d731 /activerecord
parentcfee9feee423947cf9e6d2508a85507cca6f75f3 (diff)
downloadrails-59c3ed1b3f90db197000e3867f255781a02412e7.tar.gz
rails-59c3ed1b3f90db197000e3867f255781a02412e7.tar.bz2
rails-59c3ed1b3f90db197000e3867f255781a02412e7.zip
MySQL: Raise ActiveRecord::InvalidForeignKey for foreign-key constraint violations on delete
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb3
-rw-r--r--activerecord/lib/active_record/errors.rb3
-rw-r--r--activerecord/test/cases/adapter_test.rb21
3 files changed, 20 insertions, 7 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index 284b38ed7b..9de8242a58 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -619,6 +619,7 @@ module ActiveRecord
ER_DUP_ENTRY = 1062
ER_NOT_NULL_VIOLATION = 1048
ER_DO_NOT_HAVE_DEFAULT = 1364
+ ER_ROW_IS_REFERENCED_2 = 1451
ER_NO_REFERENCED_ROW_2 = 1452
ER_DATA_TOO_LONG = 1406
ER_OUT_OF_RANGE = 1264
@@ -633,7 +634,7 @@ module ActiveRecord
case error_number(exception)
when ER_DUP_ENTRY
RecordNotUnique.new(message)
- when ER_NO_REFERENCED_ROW_2
+ when ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
InvalidForeignKey.new(message)
when ER_CANNOT_ADD_FOREIGN
mismatched_foreign_key(message)
diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb
index c2a180c939..f61bc7b9e8 100644
--- a/activerecord/lib/active_record/errors.rb
+++ b/activerecord/lib/active_record/errors.rb
@@ -111,7 +111,8 @@ module ActiveRecord
class RecordNotUnique < WrappedDatabaseException
end
- # Raised when a record cannot be inserted or updated because it references a non-existent record.
+ # Raised when a record cannot be inserted or updated because it references a non-existent record,
+ # or when a record cannot be deleted because a parent record references it.
class InvalidForeignKey < WrappedDatabaseException
end
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb
index 79642f5871..59b99351d1 100644
--- a/activerecord/test/cases/adapter_test.rb
+++ b/activerecord/test/cases/adapter_test.rb
@@ -304,6 +304,8 @@ module ActiveRecord
class AdapterForeignKeyTest < ActiveRecord::TestCase
self.use_transactional_tests = false
+ fixtures :fk_test_has_pk
+
def setup
@connection = ActiveRecord::Base.connection
end
@@ -322,7 +324,7 @@ module ActiveRecord
assert_not_nil error.cause
end
- def test_foreign_key_violations_are_translated_to_specific_exception
+ def test_foreign_key_violations_on_insert_are_translated_to_specific_exception
error = assert_raises(ActiveRecord::InvalidForeignKey) do
insert_into_fk_test_has_fk
end
@@ -330,6 +332,16 @@ module ActiveRecord
assert_not_nil error.cause
end
+ def test_foreign_key_violations_on_delete_are_translated_to_specific_exception
+ insert_into_fk_test_has_fk fk_id: 1
+
+ error = assert_raises(ActiveRecord::InvalidForeignKey) do
+ @connection.execute "DELETE FROM fk_test_has_pk WHERE pk_id = 1"
+ end
+
+ assert_not_nil error.cause
+ end
+
def test_disable_referential_integrity
assert_nothing_raised do
@connection.disable_referential_integrity do
@@ -342,14 +354,13 @@ module ActiveRecord
end
private
-
- def insert_into_fk_test_has_fk
+ def insert_into_fk_test_has_fk(fk_id: 0)
# Oracle adapter uses prefetched primary key values from sequence and passes them to connection adapter insert method
if @connection.prefetch_primary_key?
id_value = @connection.next_sequence_value(@connection.default_sequence_name("fk_test_has_fk", "id"))
- @connection.execute "INSERT INTO fk_test_has_fk (id,fk_id) VALUES (#{id_value},0)"
+ @connection.execute "INSERT INTO fk_test_has_fk (id,fk_id) VALUES (#{id_value},#{fk_id})"
else
- @connection.execute "INSERT INTO fk_test_has_fk (fk_id) VALUES (0)"
+ @connection.execute "INSERT INTO fk_test_has_fk (fk_id) VALUES (#{fk_id})"
end
end
end