strong_migrations 0.6.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +241 -1
- data/LICENSE.txt +1 -1
- data/README.md +551 -240
- data/lib/generators/strong_migrations/install_generator.rb +42 -0
- data/lib/generators/strong_migrations/templates/initializer.rb.tt +25 -0
- data/lib/strong_migrations/adapters/abstract_adapter.rb +76 -0
- data/lib/strong_migrations/adapters/mariadb_adapter.rb +32 -0
- data/lib/strong_migrations/adapters/mysql_adapter.rb +112 -0
- data/lib/strong_migrations/adapters/postgresql_adapter.rb +237 -0
- data/lib/strong_migrations/checker.rb +200 -291
- data/lib/strong_migrations/checks.rb +501 -0
- data/lib/strong_migrations/error_messages.rb +276 -0
- data/lib/strong_migrations/migration.rb +20 -2
- data/lib/strong_migrations/{database_tasks.rb → migration_context.rb} +20 -2
- data/lib/strong_migrations/migrator.rb +21 -0
- data/lib/strong_migrations/railtie.rb +0 -4
- data/lib/strong_migrations/safe_methods.rb +150 -0
- data/lib/strong_migrations/schema_dumper.rb +32 -0
- data/lib/strong_migrations/version.rb +1 -1
- data/lib/strong_migrations.rb +52 -216
- data/lib/tasks/strong_migrations.rake +2 -13
- metadata +19 -70
- data/lib/strong_migrations/alphabetize_columns.rb +0 -11
- data/lib/strong_migrations/migration_helpers.rb +0 -117
@@ -1,368 +1,277 @@
|
|
1
1
|
module StrongMigrations
|
2
2
|
class Checker
|
3
|
-
|
3
|
+
include Checks
|
4
|
+
include SafeMethods
|
5
|
+
|
6
|
+
attr_accessor :direction, :transaction_disabled, :timeouts_set
|
7
|
+
|
8
|
+
class << self
|
9
|
+
attr_accessor :safe
|
10
|
+
end
|
4
11
|
|
5
12
|
def initialize(migration)
|
6
13
|
@migration = migration
|
14
|
+
reset
|
15
|
+
end
|
16
|
+
|
17
|
+
def reset
|
7
18
|
@new_tables = []
|
8
|
-
@
|
19
|
+
@new_columns = []
|
9
20
|
@timeouts_set = false
|
21
|
+
@committed = false
|
22
|
+
@transaction_disabled = false
|
23
|
+
@skip_retries = false
|
10
24
|
end
|
11
25
|
|
12
|
-
def safety_assured
|
13
|
-
previous_value =
|
26
|
+
def self.safety_assured
|
27
|
+
previous_value = safe
|
14
28
|
begin
|
15
|
-
|
29
|
+
self.safe = true
|
16
30
|
yield
|
17
31
|
ensure
|
18
|
-
|
32
|
+
self.safe = previous_value
|
19
33
|
end
|
20
34
|
end
|
21
35
|
|
22
|
-
def perform(method, *args)
|
36
|
+
def perform(method, *args, &block)
|
37
|
+
return yield if skip?
|
38
|
+
|
39
|
+
check_adapter
|
40
|
+
check_version_supported
|
23
41
|
set_timeouts
|
42
|
+
check_lock_timeout
|
24
43
|
|
25
|
-
|
44
|
+
if !safe? || safe_by_default_method?(method)
|
45
|
+
# TODO better pattern
|
46
|
+
# see checks.rb for methods
|
26
47
|
case method
|
27
|
-
when :
|
28
|
-
|
29
|
-
case method
|
30
|
-
when :remove_timestamps
|
31
|
-
["created_at", "updated_at"]
|
32
|
-
when :remove_column
|
33
|
-
[args[1].to_s]
|
34
|
-
when :remove_columns
|
35
|
-
args[1..-1].map(&:to_s)
|
36
|
-
else
|
37
|
-
options = args[2] || {}
|
38
|
-
reference = args[1]
|
39
|
-
cols = []
|
40
|
-
cols << "#{reference}_type" if options[:polymorphic]
|
41
|
-
cols << "#{reference}_id"
|
42
|
-
cols
|
43
|
-
end
|
44
|
-
|
45
|
-
code = "self.ignored_columns = #{columns.inspect}"
|
46
|
-
|
47
|
-
raise_error :remove_column,
|
48
|
-
model: args[0].to_s.classify,
|
49
|
-
code: code,
|
50
|
-
command: command_str(method, args),
|
51
|
-
column_suffix: columns.size > 1 ? "s" : ""
|
52
|
-
when :change_table
|
53
|
-
raise_error :change_table, header: "Possibly dangerous operation"
|
54
|
-
when :rename_table
|
55
|
-
raise_error :rename_table
|
56
|
-
when :rename_column
|
57
|
-
raise_error :rename_column
|
58
|
-
when :add_index
|
59
|
-
table, columns, options = args
|
60
|
-
options ||= {}
|
61
|
-
|
62
|
-
if columns.is_a?(Array) && columns.size > 3 && !options[:unique]
|
63
|
-
raise_error :add_index_columns, header: "Best practice"
|
64
|
-
end
|
65
|
-
if postgresql? && options[:algorithm] != :concurrently && !new_table?(table)
|
66
|
-
raise_error :add_index, command: command_str("add_index", [table, columns, options.merge(algorithm: :concurrently)])
|
67
|
-
end
|
68
|
-
when :remove_index
|
69
|
-
table, options = args
|
70
|
-
unless options.is_a?(Hash)
|
71
|
-
options = {column: options}
|
72
|
-
end
|
73
|
-
options ||= {}
|
74
|
-
|
75
|
-
if postgresql? && options[:algorithm] != :concurrently && !new_table?(table)
|
76
|
-
raise_error :remove_index, command: command_str("remove_index", [table, options.merge(algorithm: :concurrently)])
|
77
|
-
end
|
48
|
+
when :add_check_constraint
|
49
|
+
check_add_check_constraint(*args)
|
78
50
|
when :add_column
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
end
|
91
|
-
|
92
|
-
raise_error :add_column_default,
|
93
|
-
add_command: command_str("add_column", [table, column, type, options.except(:default)]),
|
94
|
-
change_command: command_str("change_column_default", [table, column, default]),
|
95
|
-
remove_command: command_str("remove_column", [table, column]),
|
96
|
-
code: backfill_code(table, column, default),
|
97
|
-
append: append
|
98
|
-
end
|
99
|
-
|
100
|
-
if type.to_s == "json" && postgresql?
|
101
|
-
raise_error :add_column_json
|
102
|
-
end
|
51
|
+
check_add_column(*args)
|
52
|
+
when :add_exclusion_constraint
|
53
|
+
check_add_exclusion_constraint(*args)
|
54
|
+
when :add_foreign_key
|
55
|
+
check_add_foreign_key(*args)
|
56
|
+
when :add_index
|
57
|
+
check_add_index(*args)
|
58
|
+
when :add_reference, :add_belongs_to
|
59
|
+
check_add_reference(method, *args)
|
60
|
+
when :add_unique_constraint
|
61
|
+
check_add_unique_constraint(*args)
|
103
62
|
when :change_column
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
63
|
+
check_change_column(*args)
|
64
|
+
when :change_column_default
|
65
|
+
check_change_column_default(*args)
|
66
|
+
when :change_column_null
|
67
|
+
check_change_column_null(*args)
|
68
|
+
when :change_table
|
69
|
+
check_change_table
|
70
|
+
when :create_join_table
|
71
|
+
check_create_join_table(*args)
|
113
72
|
when :create_table
|
114
|
-
|
115
|
-
options ||= {}
|
116
|
-
|
117
|
-
raise_error :create_table if options[:force]
|
118
|
-
|
119
|
-
# keep track of new tables of add_index check
|
120
|
-
@new_tables << table.to_s
|
121
|
-
when :add_reference, :add_belongs_to
|
122
|
-
table, reference, options = args
|
123
|
-
options ||= {}
|
124
|
-
|
125
|
-
index_value = options.fetch(:index, true)
|
126
|
-
concurrently_set = index_value.is_a?(Hash) && index_value[:algorithm] == :concurrently
|
127
|
-
|
128
|
-
if postgresql? && index_value && !concurrently_set
|
129
|
-
columns = options[:polymorphic] ? [:"#{reference}_type", :"#{reference}_id"] : :"#{reference}_id"
|
130
|
-
|
131
|
-
if index_value.is_a?(Hash)
|
132
|
-
options[:index] = options[:index].merge(algorithm: :concurrently)
|
133
|
-
else
|
134
|
-
options = options.merge(index: {algorithm: :concurrently})
|
135
|
-
end
|
136
|
-
|
137
|
-
raise_error :add_reference, command: command_str(method, [table, reference, options])
|
138
|
-
end
|
73
|
+
check_create_table(*args)
|
139
74
|
when :execute
|
140
|
-
|
141
|
-
when :
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
elsif !default.nil?
|
159
|
-
raise_error :change_column_null,
|
160
|
-
code: backfill_code(table, column, default)
|
161
|
-
end
|
162
|
-
end
|
163
|
-
when :add_foreign_key
|
164
|
-
from_table, to_table, options = args
|
165
|
-
options ||= {}
|
166
|
-
|
167
|
-
# always validated before 5.2
|
168
|
-
validate = options.fetch(:validate, true) || ActiveRecord::VERSION::STRING < "5.2"
|
169
|
-
|
170
|
-
if postgresql? && validate
|
171
|
-
if helpers?
|
172
|
-
raise_error :add_foreign_key_helper,
|
173
|
-
command: command_str(:add_foreign_key_safely, [from_table, to_table, options])
|
174
|
-
elsif ActiveRecord::VERSION::STRING < "5.2"
|
175
|
-
# fk name logic from rails
|
176
|
-
primary_key = options[:primary_key] || "id"
|
177
|
-
column = options[:column] || "#{to_table.to_s.singularize}_id"
|
178
|
-
hashed_identifier = Digest::SHA256.hexdigest("#{from_table}_#{column}_fk").first(10)
|
179
|
-
fk_name = options[:name] || "fk_rails_#{hashed_identifier}"
|
180
|
-
|
181
|
-
raise_error :add_foreign_key,
|
182
|
-
add_foreign_key_code: constraint_str("ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s) NOT VALID", [from_table, fk_name, column, to_table, primary_key]),
|
183
|
-
validate_foreign_key_code: constraint_str("ALTER TABLE %s VALIDATE CONSTRAINT %s", [from_table, fk_name])
|
184
|
-
else
|
185
|
-
raise_error :add_foreign_key,
|
186
|
-
add_foreign_key_code: command_str("add_foreign_key", [from_table, to_table, options.merge(validate: false)]),
|
187
|
-
validate_foreign_key_code: command_str("validate_foreign_key", [from_table, to_table])
|
188
|
-
end
|
189
|
-
end
|
75
|
+
check_execute
|
76
|
+
when :remove_column, :remove_columns, :remove_timestamps, :remove_reference, :remove_belongs_to
|
77
|
+
check_remove_column(method, *args)
|
78
|
+
when :remove_index
|
79
|
+
check_remove_index(*args)
|
80
|
+
when :rename_column
|
81
|
+
check_rename_column
|
82
|
+
when :rename_table
|
83
|
+
check_rename_table
|
84
|
+
when :validate_check_constraint
|
85
|
+
check_validate_check_constraint
|
86
|
+
when :validate_foreign_key
|
87
|
+
check_validate_foreign_key
|
88
|
+
when :commit_db_transaction
|
89
|
+
# if committed, likely no longer in DDL transaction
|
90
|
+
# and no longer eligible to be retried at migration level
|
91
|
+
# okay to have false positives
|
92
|
+
@committed = true
|
190
93
|
end
|
191
94
|
|
192
|
-
|
193
|
-
|
95
|
+
if !safe?
|
96
|
+
# custom checks
|
97
|
+
StrongMigrations.checks.each do |check|
|
98
|
+
@migration.instance_exec(method, args, &check)
|
99
|
+
end
|
194
100
|
end
|
195
101
|
end
|
196
102
|
|
197
|
-
result =
|
103
|
+
result =
|
104
|
+
if retry_lock_timeouts?(method)
|
105
|
+
# TODO figure out how to handle methods that generate multiple statements
|
106
|
+
# like add_reference(table, ref, index: {algorithm: :concurrently})
|
107
|
+
# lock timeout after first statement will cause retry to fail
|
108
|
+
retry_lock_timeouts { perform_method(method, *args, &block) }
|
109
|
+
else
|
110
|
+
perform_method(method, *args, &block)
|
111
|
+
end
|
198
112
|
|
113
|
+
# outdated statistics + a new index can hurt performance of existing queries
|
199
114
|
if StrongMigrations.auto_analyze && direction == :up && method == :add_index
|
200
|
-
|
201
|
-
connection.execute "ANALYZE VERBOSE #{connection.quote_table_name(args[0].to_s)}"
|
202
|
-
elsif mariadb? || mysql?
|
203
|
-
connection.execute "ANALYZE TABLE #{connection.quote_table_name(args[0].to_s)}"
|
204
|
-
end
|
115
|
+
adapter.analyze_table(args[0])
|
205
116
|
end
|
206
117
|
|
207
118
|
result
|
208
119
|
end
|
209
120
|
|
210
|
-
def
|
211
|
-
if
|
212
|
-
|
213
|
-
statement =
|
214
|
-
if postgresql?
|
215
|
-
"SET statement_timeout TO #{connection.quote(StrongMigrations.statement_timeout)}"
|
216
|
-
elsif mysql?
|
217
|
-
"SET max_execution_time = #{connection.quote(StrongMigrations.statement_timeout.to_i * 1000)}"
|
218
|
-
elsif mariadb?
|
219
|
-
"SET max_statement_time = #{connection.quote(StrongMigrations.statement_timeout)}"
|
220
|
-
else
|
221
|
-
raise StrongMigrations::Error, "Statement timeout not supported for this database"
|
222
|
-
end
|
223
|
-
|
224
|
-
connection.select_all(statement)
|
225
|
-
end
|
226
|
-
|
227
|
-
if StrongMigrations.lock_timeout
|
228
|
-
statement =
|
229
|
-
if postgresql?
|
230
|
-
"SET lock_timeout TO #{connection.quote(StrongMigrations.lock_timeout)}"
|
231
|
-
elsif mysql? || mariadb?
|
232
|
-
"SET lock_wait_timeout = #{connection.quote(StrongMigrations.lock_timeout)}"
|
233
|
-
else
|
234
|
-
raise StrongMigrations::Error, "Lock timeout not supported for this database"
|
235
|
-
end
|
236
|
-
|
237
|
-
connection.select_all(statement)
|
238
|
-
end
|
239
|
-
|
240
|
-
@timeouts_set = true
|
121
|
+
def perform_method(method, *args)
|
122
|
+
if StrongMigrations.remove_invalid_indexes && direction == :up && method == :add_index && postgresql?
|
123
|
+
remove_invalid_index_if_needed(*args)
|
241
124
|
end
|
125
|
+
yield
|
242
126
|
end
|
243
127
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
128
|
+
def retry_lock_timeouts(check_committed: false)
|
129
|
+
retries = 0
|
130
|
+
begin
|
131
|
+
yield
|
132
|
+
rescue ActiveRecord::LockWaitTimeout => e
|
133
|
+
if retries < StrongMigrations.lock_timeout_retries && !(check_committed && @committed)
|
134
|
+
retries += 1
|
135
|
+
delay = StrongMigrations.lock_timeout_retry_delay
|
136
|
+
@migration.say("Lock timeout. Retrying in #{delay} seconds...")
|
137
|
+
sleep(delay)
|
138
|
+
retry
|
139
|
+
end
|
140
|
+
raise e
|
141
|
+
end
|
256
142
|
end
|
257
143
|
|
258
144
|
def version_safe?
|
259
145
|
version && version <= StrongMigrations.start_after
|
260
146
|
end
|
261
147
|
|
262
|
-
def
|
263
|
-
|
148
|
+
def skip?
|
149
|
+
StrongMigrations.skipped_databases.map(&:to_s).include?(db_config_name)
|
264
150
|
end
|
265
151
|
|
266
|
-
|
267
|
-
@postgresql_version ||= begin
|
268
|
-
target_version(StrongMigrations.target_postgresql_version) do
|
269
|
-
connection.select_all("SHOW server_version").first["server_version"]
|
270
|
-
end
|
271
|
-
end
|
272
|
-
end
|
152
|
+
private
|
273
153
|
|
274
|
-
def
|
275
|
-
|
276
|
-
end
|
154
|
+
def check_adapter
|
155
|
+
return if defined?(@adapter_checked)
|
277
156
|
|
278
|
-
|
279
|
-
|
280
|
-
target_version(StrongMigrations.target_mysql_version) do
|
281
|
-
connection.select_all("SELECT VERSION()").first["VERSION()"].split("-").first
|
282
|
-
end
|
157
|
+
if adapter.instance_of?(Adapters::AbstractAdapter)
|
158
|
+
warn "[strong_migrations] Unsupported adapter: #{connection.adapter_name}. Use StrongMigrations.skip_database(#{db_config_name.to_sym.inspect}) to silence this warning."
|
283
159
|
end
|
284
|
-
end
|
285
160
|
|
286
|
-
|
287
|
-
connection.adapter_name =~ /mysql/i && connection.try(:mariadb?)
|
161
|
+
@adapter_checked = true
|
288
162
|
end
|
289
163
|
|
290
|
-
def
|
291
|
-
|
292
|
-
|
293
|
-
|
164
|
+
def check_version_supported
|
165
|
+
return if defined?(@version_checked)
|
166
|
+
|
167
|
+
min_version = adapter.min_version
|
168
|
+
if min_version
|
169
|
+
version = adapter.server_version
|
170
|
+
if version < Gem::Version.new(min_version)
|
171
|
+
raise UnsupportedVersion, "#{adapter.name} version (#{version}) not supported in this version of Strong Migrations (#{StrongMigrations::VERSION})"
|
294
172
|
end
|
295
173
|
end
|
296
|
-
end
|
297
174
|
|
298
|
-
|
299
|
-
version =
|
300
|
-
if target_version && defined?(Rails) && (Rails.env.development? || Rails.env.test?)
|
301
|
-
target_version.to_s
|
302
|
-
else
|
303
|
-
yield
|
304
|
-
end
|
305
|
-
Gem::Version.new(version)
|
175
|
+
@version_checked = true
|
306
176
|
end
|
307
177
|
|
308
|
-
def
|
309
|
-
|
310
|
-
end
|
178
|
+
def set_timeouts
|
179
|
+
return if @timeouts_set
|
311
180
|
|
312
|
-
|
313
|
-
|
181
|
+
if StrongMigrations.statement_timeout
|
182
|
+
adapter.set_statement_timeout(StrongMigrations.statement_timeout)
|
183
|
+
end
|
184
|
+
if StrongMigrations.lock_timeout
|
185
|
+
adapter.set_lock_timeout(StrongMigrations.lock_timeout)
|
186
|
+
end
|
314
187
|
|
315
|
-
|
188
|
+
@timeouts_set = true
|
189
|
+
end
|
316
190
|
|
317
|
-
|
318
|
-
|
319
|
-
vars[:base_model] = "ApplicationRecord"
|
191
|
+
def check_lock_timeout
|
192
|
+
return if defined?(@lock_timeout_checked)
|
320
193
|
|
321
|
-
|
322
|
-
|
323
|
-
vars[:append] = vars[:append].gsub(/%(?!{)/, "%%") % vars
|
194
|
+
if StrongMigrations.lock_timeout_limit
|
195
|
+
adapter.check_lock_timeout(StrongMigrations.lock_timeout_limit)
|
324
196
|
end
|
325
197
|
|
326
|
-
|
327
|
-
@migration.stop!(message.gsub(/%(?!{)/, "%%") % vars, header: header || "Dangerous operation detected")
|
198
|
+
@lock_timeout_checked = true
|
328
199
|
end
|
329
200
|
|
330
|
-
def
|
331
|
-
|
332
|
-
|
333
|
-
|
201
|
+
def safe?
|
202
|
+
self.class.safe || ENV["SAFETY_ASSURED"] || (direction == :down && !StrongMigrations.check_down) || version_safe? || @migration.reverting?
|
203
|
+
end
|
204
|
+
|
205
|
+
def version
|
206
|
+
@migration.version
|
334
207
|
end
|
335
208
|
|
336
|
-
def
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
# pretty index: {algorithm: :concurrently}
|
346
|
-
"#{k}: {#{v.map { |k2, v2| "#{k2}: #{v2.inspect}" }.join(", ")}}"
|
209
|
+
def adapter
|
210
|
+
@adapter ||= begin
|
211
|
+
cls =
|
212
|
+
case connection.adapter_name
|
213
|
+
when /postg/i # PostgreSQL, PostGIS
|
214
|
+
Adapters::PostgreSQLAdapter
|
215
|
+
when /mysql|trilogy/i
|
216
|
+
if connection.try(:mariadb?)
|
217
|
+
Adapters::MariaDBAdapter
|
347
218
|
else
|
348
|
-
|
219
|
+
Adapters::MySQLAdapter
|
349
220
|
end
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
221
|
+
else
|
222
|
+
Adapters::AbstractAdapter
|
223
|
+
end
|
224
|
+
|
225
|
+
cls.new(self)
|
354
226
|
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def connection
|
230
|
+
@migration.connection
|
231
|
+
end
|
232
|
+
|
233
|
+
def db_config_name
|
234
|
+
connection.pool.db_config.name
|
235
|
+
end
|
355
236
|
|
356
|
-
|
237
|
+
def retry_lock_timeouts?(method)
|
238
|
+
(
|
239
|
+
StrongMigrations.lock_timeout_retries > 0 &&
|
240
|
+
!in_transaction? &&
|
241
|
+
method != :transaction &&
|
242
|
+
!@skip_retries
|
243
|
+
)
|
357
244
|
end
|
358
245
|
|
359
|
-
def
|
360
|
-
|
361
|
-
|
246
|
+
def without_retries
|
247
|
+
previous_value = @skip_retries
|
248
|
+
begin
|
249
|
+
@skip_retries = true
|
250
|
+
yield
|
251
|
+
ensure
|
252
|
+
@skip_retries = previous_value
|
253
|
+
end
|
362
254
|
end
|
363
255
|
|
364
|
-
|
365
|
-
|
256
|
+
# REINDEX INDEX CONCURRENTLY leaves a new invalid index if it fails, so use remove_index instead
|
257
|
+
def remove_invalid_index_if_needed(*args)
|
258
|
+
options = args.extract_options!
|
259
|
+
|
260
|
+
# ensures has same options as existing index
|
261
|
+
# check args to avoid errors with index_exists?
|
262
|
+
return unless args.size == 2 && connection.index_exists?(*args, **options.merge(valid: false))
|
263
|
+
|
264
|
+
table, columns = args
|
265
|
+
index_name = options.fetch(:name, connection.index_name(table, columns))
|
266
|
+
|
267
|
+
# valid option is ignored for Active Record < 7.1, so check name as well
|
268
|
+
return if ar_version < 7.1 && !adapter.index_invalid?(table, index_name)
|
269
|
+
|
270
|
+
@migration.say("Attempting to remove invalid index")
|
271
|
+
without_retries do
|
272
|
+
# TODO pass index schema for extra safety?
|
273
|
+
@migration.remove_index(table, **{name: index_name}.merge(options.slice(:algorithm)))
|
274
|
+
end
|
366
275
|
end
|
367
276
|
end
|
368
277
|
end
|