Class: RuboCop::Cop::Discourse::NoAddReferenceOrAliasesActiveRecordMigration
- Inherits:
-
Base
- Object
- Base
- RuboCop::Cop::Discourse::NoAddReferenceOrAliasesActiveRecordMigration
- Defined in:
- lib/rubocop/cop/discourse/no_add_reference_active_record_migrations.rb
Overview
The methods:
- add_reference
- add_belongs_to
- t.references
- t.belongs_to
in ActiveRecord migrations are magic, and they all do some unexpected things in the background. For example, by default add_reference adds an index at the same time, but not concurrently, which is a nightmare for large tables.
Instead, inside a disable_ddl_transaction! migration we should create the new column (with any defaults and options required) and the index concurrently using hand-written SQL. This also allows us to handle IF NOT EXISTS cases, which enable re-runnable migrations. Also we can pick a better name for the index at the same time.
bad
def up add_reference :posts, :image_upload
or add_belongs_to :posts, :image_upload
or t.references :image_upload when doing create_table do |t|
or t.belongs_to :image_upload when doing create_table do |t|
end
good
disable_ddl_transaction! def up execute <<~SQL ALTER TABLE posts ADD COLUMN IF NOT EXISTS image_upload_id bigint SQL
execute <<~SQL
CREATE INDEX CONCURRENTLY IF NOT EXISTS
index_posts_on_image_upload_id ON posts USING btree (image_upload_id)
SQL
end
Constant Summary collapse
- MSG =
<<~MSG AR methods add_reference, add_belongs_to, t.references, and t.belongs_to are high-risk for large tables and have too many background magic operations. Instead, write a disable_ddl_transactions! migration and write custom SQL to add the new column and CREATE INDEX CONCURRENTLY. Use the IF NOT EXISTS clause to make the migration re-runnable if it fails partway through. MSG
Instance Method Summary collapse
Instance Method Details
#on_send(node) ⇒ Object
72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/rubocop/cop/discourse/no_add_reference_active_record_migrations.rb', line 72 def on_send(node) if [ using_add_reference?(node), using_add_belongs_to?(node), using_t_references?(node), using_t_belongs_to?(node), ].none? return end add_offense(node, message: MSG) end |