@@ -35,8 +35,30 @@ def check_all_foreign_keys_valid!
3535
3636 def disable_referential_integrity
3737 foreign_keys = all_foreign_keys
38- dropped_fks = foreign_keys
3938
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 )
4062 statements = foreign_keys . map do |foreign_key |
4163 # We do not use the `#remove_foreign_key` method here because it
4264 # checks for foreign keys existance in the schema cache. This method
@@ -47,54 +69,28 @@ def disable_referential_integrity
4769 schema_creation . accept ( at )
4870 end
4971 execute_batch ( statements , "Disable referential integrity -> remove foreign keys" )
72+ end
5073
51- STDOUT . puts "BEFORE FK"
52- yield
53- STDOUT . puts "AFTER FK"
54-
55- # Prefixes and suffixes are added in add_foreign_key
56- # in AR7+ so we need to temporarily disable them here,
57- # otherwise prefixes/suffixes will be erroneously added.
58- old_prefix = ActiveRecord ::Base . table_name_prefix
59- 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 ] }
6084
61- ActiveRecord ::Base . table_name_prefix = ""
62- 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
6388
64- begin
65- # Avoid having PG:DuplicateObject error if a test is ran in transaction.
66- # TODO: verify that there is no cache issue related to running this (e.g: fk
67- # still in cache but not in db)
68- #
69- # We avoid using `foreign_key_exists?` here because it checks the schema cache
70- # for every key. This method is performance critical for the test suite, hence
71- # we use the `#all_foreign_keys` method that only make one query to the database.
72- already_inserted_foreign_keys = all_foreign_keys
73- new_fks = [ ]
74- statements = foreign_keys . map do |foreign_key |
75- next if already_inserted_foreign_keys . any? { |fk | fk . from_table == foreign_key . from_table && fk . options [ :name ] == foreign_key . options [ :name ] }
76-
77- options = foreign_key_options ( foreign_key . from_table , foreign_key . to_table , foreign_key . options )
78- at = create_alter_table foreign_key . from_table
79- at . add_foreign_key foreign_key . to_table , options
80-
81- new_fks << foreign_key
82- schema_creation . accept ( at )
83- end
84- execute_batch ( statements . compact , "Disable referential integrity -> add foreign keys" )
85-
86- new_fks = new_fks . map { _1 . from_table + " -> " + _1 . to_table + ( _1 . options [ :name ] ? " (#{ _1 . options [ :name ] } )" : "" ) }
87- dropped_fks = dropped_fks . map { _1 . from_table + " -> " + _1 . to_table + ( _1 . options [ :name ] ? " (#{ _1 . options [ :name ] } )" : "" ) }
88- STDOUT . puts "FK DIFF NEW:\n " , ( new_fks - dropped_fks ) . map { "\t #{ _1 } " } if ( new_fks - dropped_fks ) . any?
89- STDOUT . puts "FK DIFF OLD:\n " , ( dropped_fks - new_fks ) . map { "\t #{ _1 } " } if ( dropped_fks - new_fks ) . any?
90- ensure
91- ActiveRecord ::Base . table_name_prefix = old_prefix
92- ActiveRecord ::Base . table_name_suffix = old_suffix
89+ schema_creation . accept ( at )
9390 end
91+ execute_batch ( statements . compact , "Disable referential integrity -> add foreign keys" )
9492 end
9593
96- private
97-
9894 # NOTE: Copy/paste of the `#foreign_keys(table)` method adapted
9995 # to return every single foreign key in the database.
10096 def all_foreign_keys
0 commit comments