@@ -36,6 +36,29 @@ def check_all_foreign_keys_valid!
3636 def disable_referential_integrity
3737 foreign_keys = all_foreign_keys
3838
39+ remove_foreign_keys ( foreign_keys )
40+
41+ # Prefixes and suffixes are added in add_foreign_key
42+ # in AR7+ so we need to temporarily disable them here,
43+ # otherwise prefixes/suffixes will be erroneously added.
44+ old_prefix = ActiveRecord ::Base . table_name_prefix
45+ old_suffix = ActiveRecord ::Base . table_name_suffix
46+
47+ begin
48+ yield
49+ ensure
50+ ActiveRecord ::Base . table_name_prefix = ""
51+ ActiveRecord ::Base . table_name_suffix = ""
52+ add_foreign_keys ( foreign_keys ) # Never raises.
53+
54+ ActiveRecord ::Base . table_name_prefix = old_prefix if defined? ( old_prefix )
55+ ActiveRecord ::Base . table_name_suffix = old_suffix if defined? ( old_suffix )
56+ end
57+ end
58+
59+ private
60+
61+ def remove_foreign_keys ( foreign_keys )
3962 statements = foreign_keys . map do |foreign_key |
4063 # We do not use the `#remove_foreign_key` method here because it
4164 # checks for foreign keys existance in the schema cache. This method
@@ -46,45 +69,28 @@ def disable_referential_integrity
4669 schema_creation . accept ( at )
4770 end
4871 execute_batch ( statements , "Disable referential integrity -> remove foreign keys" )
72+ end
4973
50- yield
51-
52- # Prefixes and suffixes are added in add_foreign_key
53- # in AR7+ so we need to temporarily disable them here,
54- # otherwise prefixes/suffixes will be erroneously added.
55- old_prefix = ActiveRecord ::Base . table_name_prefix
56- old_suffix = ActiveRecord ::Base . table_name_suffix
74+ # NOTE: This method should never raise, otherwise we risk polluting table name
75+ # prefixes and suffixes. The good thing is: if this happens, tests will crash
76+ # hard, no way we miss it.
77+ def add_foreign_keys ( foreign_keys )
78+ # We avoid using `foreign_key_exists?` here because it checks the schema cache
79+ # for every key. This method is performance critical for the test suite, hence
80+ # we use the `#all_foreign_keys` method that only make one query to the database.
81+ already_inserted_foreign_keys = all_foreign_keys
82+ statements = foreign_keys . map do |foreign_key |
83+ next if already_inserted_foreign_keys . any? { |fk | fk . from_table == foreign_key . from_table && fk . options [ :name ] == foreign_key . options [ :name ] }
5784
58- ActiveRecord ::Base . table_name_prefix = ""
59- ActiveRecord ::Base . table_name_suffix = ""
85+ options = foreign_key_options ( foreign_key . from_table , foreign_key . to_table , foreign_key . options )
86+ at = create_alter_table foreign_key . from_table
87+ at . add_foreign_key foreign_key . to_table , options
6088
61- begin
62- # Avoid having PG:DuplicateObject error if a test is ran in transaction.
63- # TODO: verify that there is no cache issue related to running this (e.g: fk
64- # still in cache but not in db)
65- #
66- # We avoid using `foreign_key_exists?` here because it checks the schema cache
67- # for every key. This method is performance critical for the test suite, hence
68- # we use the `#all_foreign_keys` method that only make one query to the database.
69- already_inserted_foreign_keys = all_foreign_keys
70- statements = foreign_keys . map do |foreign_key |
71- next if already_inserted_foreign_keys . any? { |fk | fk . from_table == foreign_key . from_table && fk . options [ :name ] == foreign_key . options [ :name ] }
72-
73- options = foreign_key_options ( foreign_key . from_table , foreign_key . to_table , foreign_key . options )
74- at = create_alter_table foreign_key . from_table
75- at . add_foreign_key foreign_key . to_table , options
76-
77- schema_creation . accept ( at )
78- end
79- execute_batch ( statements . compact , "Disable referential integrity -> add foreign keys" )
80- ensure
81- ActiveRecord ::Base . table_name_prefix = old_prefix
82- ActiveRecord ::Base . table_name_suffix = old_suffix
89+ schema_creation . accept ( at )
8390 end
91+ execute_batch ( statements . compact , "Disable referential integrity -> add foreign keys" )
8492 end
8593
86- private
87-
8894 # NOTE: Copy/paste of the `#foreign_keys(table)` method adapted
8995 # to return every single foreign key in the database.
9096 def all_foreign_keys
0 commit comments