strong_migrations 0.6.8 → 1.2.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 +93 -0
- data/LICENSE.txt +1 -1
- data/README.md +285 -117
- data/lib/generators/strong_migrations/install_generator.rb +29 -0
- data/lib/generators/strong_migrations/templates/initializer.rb.tt +10 -3
- data/lib/strong_migrations/adapters/abstract_adapter.rb +61 -0
- data/lib/strong_migrations/adapters/mariadb_adapter.rb +29 -0
- data/lib/strong_migrations/adapters/mysql_adapter.rb +87 -0
- data/lib/strong_migrations/adapters/postgresql_adapter.rb +227 -0
- data/lib/strong_migrations/checker.rb +116 -401
- data/lib/strong_migrations/checks.rb +421 -0
- data/lib/strong_migrations/database_tasks.rb +2 -1
- data/lib/strong_migrations/error_messages.rb +227 -0
- data/lib/strong_migrations/migration.rb +10 -2
- data/lib/strong_migrations/migrator.rb +19 -0
- data/lib/strong_migrations/railtie.rb +0 -4
- data/lib/strong_migrations/safe_methods.rb +118 -0
- data/lib/strong_migrations/version.rb +1 -1
- data/lib/strong_migrations.rb +20 -180
- data/lib/tasks/strong_migrations.rake +0 -6
- metadata +19 -81
@@ -1,13 +1,16 @@
|
|
1
1
|
module StrongMigrations
|
2
2
|
class Checker
|
3
|
-
|
3
|
+
include Checks
|
4
|
+
include SafeMethods
|
5
|
+
|
6
|
+
attr_accessor :direction, :transaction_disabled, :timeouts_set
|
4
7
|
|
5
8
|
def initialize(migration)
|
6
9
|
@migration = migration
|
7
10
|
@new_tables = []
|
8
11
|
@safe = false
|
9
12
|
@timeouts_set = false
|
10
|
-
@
|
13
|
+
@committed = false
|
11
14
|
end
|
12
15
|
|
13
16
|
def safety_assured
|
@@ -21,464 +24,176 @@ module StrongMigrations
|
|
21
24
|
end
|
22
25
|
|
23
26
|
def perform(method, *args)
|
27
|
+
check_version_supported
|
24
28
|
set_timeouts
|
25
29
|
check_lock_timeout
|
26
30
|
|
27
|
-
|
31
|
+
if !safe? || safe_by_default_method?(method)
|
32
|
+
# TODO better pattern
|
33
|
+
# see checks.rb for methods
|
28
34
|
case method
|
29
|
-
when :
|
30
|
-
|
31
|
-
case method
|
32
|
-
when :remove_timestamps
|
33
|
-
["created_at", "updated_at"]
|
34
|
-
when :remove_column
|
35
|
-
[args[1].to_s]
|
36
|
-
when :remove_columns
|
37
|
-
args[1..-1].map(&:to_s)
|
38
|
-
else
|
39
|
-
options = args[2] || {}
|
40
|
-
reference = args[1]
|
41
|
-
cols = []
|
42
|
-
cols << "#{reference}_type" if options[:polymorphic]
|
43
|
-
cols << "#{reference}_id"
|
44
|
-
cols
|
45
|
-
end
|
46
|
-
|
47
|
-
code = "self.ignored_columns = #{columns.inspect}"
|
48
|
-
|
49
|
-
raise_error :remove_column,
|
50
|
-
model: args[0].to_s.classify,
|
51
|
-
code: code,
|
52
|
-
command: command_str(method, args),
|
53
|
-
column_suffix: columns.size > 1 ? "s" : ""
|
54
|
-
when :change_table
|
55
|
-
raise_error :change_table, header: "Possibly dangerous operation"
|
56
|
-
when :rename_table
|
57
|
-
raise_error :rename_table
|
58
|
-
when :rename_column
|
59
|
-
raise_error :rename_column
|
60
|
-
when :add_index
|
61
|
-
table, columns, options = args
|
62
|
-
options ||= {}
|
63
|
-
|
64
|
-
if columns.is_a?(Array) && columns.size > 3 && !options[:unique]
|
65
|
-
raise_error :add_index_columns, header: "Best practice"
|
66
|
-
end
|
67
|
-
if postgresql? && options[:algorithm] != :concurrently && !new_table?(table)
|
68
|
-
raise_error :add_index, command: command_str("add_index", [table, columns, options.merge(algorithm: :concurrently)])
|
69
|
-
end
|
70
|
-
when :remove_index
|
71
|
-
table, options = args
|
72
|
-
unless options.is_a?(Hash)
|
73
|
-
options = {column: options}
|
74
|
-
end
|
75
|
-
options ||= {}
|
76
|
-
|
77
|
-
if postgresql? && options[:algorithm] != :concurrently && !new_table?(table)
|
78
|
-
raise_error :remove_index, command: command_str("remove_index", [table, options.merge(algorithm: :concurrently)])
|
79
|
-
end
|
35
|
+
when :add_check_constraint
|
36
|
+
check_add_check_constraint(*args)
|
80
37
|
when :add_column
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
options = options.except(:null)
|
89
|
-
append = "
|
90
|
-
|
91
|
-
Then add the NOT NULL constraint in separate migrations."
|
92
|
-
end
|
93
|
-
|
94
|
-
raise_error :add_column_default,
|
95
|
-
add_command: command_str("add_column", [table, column, type, options.except(:default)]),
|
96
|
-
change_command: command_str("change_column_default", [table, column, default]),
|
97
|
-
remove_command: command_str("remove_column", [table, column]),
|
98
|
-
code: backfill_code(table, column, default),
|
99
|
-
append: append
|
100
|
-
end
|
101
|
-
|
102
|
-
if type.to_s == "json" && postgresql?
|
103
|
-
raise_error :add_column_json
|
104
|
-
end
|
38
|
+
check_add_column(*args)
|
39
|
+
when :add_foreign_key
|
40
|
+
check_add_foreign_key(*args)
|
41
|
+
when :add_index
|
42
|
+
check_add_index(*args)
|
43
|
+
when :add_reference, :add_belongs_to
|
44
|
+
check_add_reference(method, *args)
|
105
45
|
when :change_column
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
if postgresql?
|
114
|
-
case type.to_s
|
115
|
-
when "string", "text"
|
116
|
-
# safe to change limit for varchar
|
117
|
-
safe = ["character varying", "text"].include?(sql_type)
|
118
|
-
when "numeric", "decimal"
|
119
|
-
# numeric and decimal are equivalent and can be used interchangably
|
120
|
-
safe = ["numeric", "decimal"].include?(sql_type) &&
|
121
|
-
(
|
122
|
-
(
|
123
|
-
# unconstrained
|
124
|
-
!options[:precision] && !options[:scale]
|
125
|
-
) || (
|
126
|
-
# increased precision, same scale
|
127
|
-
options[:precision] && existing_column.precision &&
|
128
|
-
options[:precision] >= existing_column.precision &&
|
129
|
-
options[:scale] == existing_column.scale
|
130
|
-
)
|
131
|
-
)
|
132
|
-
when "datetime", "timestamp", "timestamptz"
|
133
|
-
safe = ["timestamp without time zone", "timestamp with time zone"].include?(sql_type) &&
|
134
|
-
postgresql_version >= Gem::Version.new("12") &&
|
135
|
-
connection.select_all("SHOW timezone").first["TimeZone"] == "UTC"
|
136
|
-
end
|
137
|
-
elsif mysql? || mariadb?
|
138
|
-
case type.to_s
|
139
|
-
when "string"
|
140
|
-
# https://dev.mysql.com/doc/refman/5.7/en/innodb-online-ddl-operations.html
|
141
|
-
# https://mariadb.com/kb/en/innodb-online-ddl-operations-with-the-instant-alter-algorithm/#changing-the-data-type-of-a-column
|
142
|
-
# increased limit, but doesn't change number of length bytes
|
143
|
-
# 1-255 = 1 byte, 256-65532 = 2 bytes, 65533+ = too big for varchar
|
144
|
-
limit = options[:limit] || 255
|
145
|
-
safe = ["varchar"].include?(sql_type) &&
|
146
|
-
limit >= existing_column.limit &&
|
147
|
-
(limit <= 255 || existing_column.limit > 255)
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
151
|
-
raise_error :change_column unless safe
|
46
|
+
check_change_column(*args)
|
47
|
+
when :change_column_null
|
48
|
+
check_change_column_null(*args)
|
49
|
+
when :change_table
|
50
|
+
check_change_table
|
51
|
+
when :create_join_table
|
52
|
+
check_create_join_table(*args)
|
152
53
|
when :create_table
|
153
|
-
|
154
|
-
options ||= {}
|
155
|
-
|
156
|
-
raise_error :create_table if options[:force]
|
157
|
-
|
158
|
-
# keep track of new tables of add_index check
|
159
|
-
@new_tables << table.to_s
|
160
|
-
when :add_reference, :add_belongs_to
|
161
|
-
table, reference, options = args
|
162
|
-
options ||= {}
|
163
|
-
|
164
|
-
if postgresql?
|
165
|
-
index_value = options.fetch(:index, true)
|
166
|
-
concurrently_set = index_value.is_a?(Hash) && index_value[:algorithm] == :concurrently
|
167
|
-
bad_index = index_value && !concurrently_set
|
168
|
-
|
169
|
-
if bad_index || options[:foreign_key]
|
170
|
-
columns = options[:polymorphic] ? [:"#{reference}_type", :"#{reference}_id"] : :"#{reference}_id"
|
171
|
-
|
172
|
-
if index_value.is_a?(Hash)
|
173
|
-
options[:index] = options[:index].merge(algorithm: :concurrently)
|
174
|
-
else
|
175
|
-
options = options.merge(index: {algorithm: :concurrently})
|
176
|
-
end
|
177
|
-
|
178
|
-
if options.delete(:foreign_key)
|
179
|
-
headline = "Adding a validated foreign key locks the table."
|
180
|
-
append = "
|
181
|
-
|
182
|
-
Then add the foreign key in separate migrations."
|
183
|
-
else
|
184
|
-
headline = "Adding an index non-concurrently locks the table."
|
185
|
-
end
|
186
|
-
|
187
|
-
raise_error :add_reference,
|
188
|
-
headline: headline,
|
189
|
-
command: command_str(method, [table, reference, options]),
|
190
|
-
append: append
|
191
|
-
end
|
192
|
-
end
|
54
|
+
check_create_table(*args)
|
193
55
|
when :execute
|
194
|
-
|
195
|
-
when :
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
validate_constraint_code << "\n #{constraint_str("ALTER TABLE %s DROP CONSTRAINT %s", [table, constraint_name])}"
|
213
|
-
end
|
214
|
-
|
215
|
-
raise_error :change_column_null_postgresql,
|
216
|
-
add_constraint_code: constraint_str("ALTER TABLE %s ADD CONSTRAINT %s CHECK (%s IS NOT NULL) NOT VALID", [table, constraint_name, column]),
|
217
|
-
validate_constraint_code: validate_constraint_code
|
218
|
-
end
|
219
|
-
elsif mysql? || mariadb?
|
220
|
-
raise_error :change_column_null_mysql
|
221
|
-
elsif !default.nil?
|
222
|
-
raise_error :change_column_null,
|
223
|
-
code: backfill_code(table, column, default)
|
224
|
-
end
|
225
|
-
end
|
226
|
-
when :add_foreign_key
|
227
|
-
from_table, to_table, options = args
|
228
|
-
options ||= {}
|
229
|
-
|
230
|
-
# always validated before 5.2
|
231
|
-
validate = options.fetch(:validate, true) || ActiveRecord::VERSION::STRING < "5.2"
|
232
|
-
|
233
|
-
if postgresql? && validate
|
234
|
-
if ActiveRecord::VERSION::STRING < "5.2"
|
235
|
-
# fk name logic from rails
|
236
|
-
primary_key = options[:primary_key] || "id"
|
237
|
-
column = options[:column] || "#{to_table.to_s.singularize}_id"
|
238
|
-
hashed_identifier = Digest::SHA256.hexdigest("#{from_table}_#{column}_fk").first(10)
|
239
|
-
fk_name = options[:name] || "fk_rails_#{hashed_identifier}"
|
240
|
-
|
241
|
-
raise_error :add_foreign_key,
|
242
|
-
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]),
|
243
|
-
validate_foreign_key_code: constraint_str("ALTER TABLE %s VALIDATE CONSTRAINT %s", [from_table, fk_name])
|
244
|
-
else
|
245
|
-
raise_error :add_foreign_key,
|
246
|
-
add_foreign_key_code: command_str("add_foreign_key", [from_table, to_table, options.merge(validate: false)]),
|
247
|
-
validate_foreign_key_code: command_str("validate_foreign_key", [from_table, to_table])
|
248
|
-
end
|
249
|
-
end
|
56
|
+
check_execute
|
57
|
+
when :remove_column, :remove_columns, :remove_timestamps, :remove_reference, :remove_belongs_to
|
58
|
+
check_remove_column(method, *args)
|
59
|
+
when :remove_index
|
60
|
+
check_remove_index(*args)
|
61
|
+
when :rename_column
|
62
|
+
check_rename_column
|
63
|
+
when :rename_table
|
64
|
+
check_rename_table
|
65
|
+
when :validate_check_constraint
|
66
|
+
check_validate_check_constraint
|
67
|
+
when :validate_foreign_key
|
68
|
+
check_validate_foreign_key
|
69
|
+
when :commit_db_transaction
|
70
|
+
# if committed, likely no longer in DDL transaction
|
71
|
+
# and no longer eligible to be retried at migration level
|
72
|
+
# okay to have false positives
|
73
|
+
@committed = true
|
250
74
|
end
|
251
75
|
|
76
|
+
# custom checks
|
252
77
|
StrongMigrations.checks.each do |check|
|
253
78
|
@migration.instance_exec(method, args, &check)
|
254
79
|
end
|
255
80
|
end
|
256
81
|
|
257
|
-
result =
|
82
|
+
result =
|
83
|
+
if retry_lock_timeouts?(method)
|
84
|
+
# TODO figure out how to handle methods that generate multiple statements
|
85
|
+
# like add_reference(table, ref, index: {algorithm: :concurrently})
|
86
|
+
# lock timeout after first statement will cause retry to fail
|
87
|
+
retry_lock_timeouts { yield }
|
88
|
+
else
|
89
|
+
yield
|
90
|
+
end
|
258
91
|
|
259
92
|
# outdated statistics + a new index can hurt performance of existing queries
|
260
93
|
if StrongMigrations.auto_analyze && direction == :up && method == :add_index
|
261
|
-
|
262
|
-
# TODO remove verbose in 0.7.0
|
263
|
-
connection.execute "ANALYZE VERBOSE #{connection.quote_table_name(args[0].to_s)}"
|
264
|
-
elsif mariadb? || mysql?
|
265
|
-
connection.execute "ANALYZE TABLE #{connection.quote_table_name(args[0].to_s)}"
|
266
|
-
end
|
94
|
+
adapter.analyze_table(args[0])
|
267
95
|
end
|
268
96
|
|
269
97
|
result
|
270
98
|
end
|
271
99
|
|
272
|
-
def
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
raise StrongMigrations::Error, "Statement timeout not supported for this database"
|
284
|
-
end
|
285
|
-
|
286
|
-
connection.select_all(statement)
|
287
|
-
end
|
288
|
-
|
289
|
-
if StrongMigrations.lock_timeout
|
290
|
-
statement =
|
291
|
-
if postgresql?
|
292
|
-
"SET lock_timeout TO #{connection.quote(postgresql_timeout(StrongMigrations.lock_timeout))}"
|
293
|
-
elsif mysql? || mariadb?
|
294
|
-
"SET lock_wait_timeout = #{connection.quote(StrongMigrations.lock_timeout)}"
|
295
|
-
else
|
296
|
-
raise StrongMigrations::Error, "Lock timeout not supported for this database"
|
297
|
-
end
|
298
|
-
|
299
|
-
connection.select_all(statement)
|
100
|
+
def retry_lock_timeouts(check_committed: false)
|
101
|
+
retries = 0
|
102
|
+
begin
|
103
|
+
yield
|
104
|
+
rescue ActiveRecord::LockWaitTimeout => e
|
105
|
+
if retries < StrongMigrations.lock_timeout_retries && !(check_committed && @committed)
|
106
|
+
retries += 1
|
107
|
+
delay = StrongMigrations.lock_timeout_retry_delay
|
108
|
+
@migration.say("Lock timeout. Retrying in #{delay} seconds...")
|
109
|
+
sleep(delay)
|
110
|
+
retry
|
300
111
|
end
|
301
|
-
|
302
|
-
@timeouts_set = true
|
112
|
+
raise e
|
303
113
|
end
|
304
114
|
end
|
305
115
|
|
306
116
|
private
|
307
117
|
|
308
|
-
def
|
309
|
-
@
|
310
|
-
end
|
118
|
+
def check_version_supported
|
119
|
+
return if defined?(@version_checked)
|
311
120
|
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
@safe || ENV["SAFETY_ASSURED"] || @migration.is_a?(ActiveRecord::Schema) || direction == :down || version_safe?
|
318
|
-
end
|
319
|
-
|
320
|
-
def version_safe?
|
321
|
-
version && version <= StrongMigrations.start_after
|
322
|
-
end
|
323
|
-
|
324
|
-
def postgresql?
|
325
|
-
connection.adapter_name =~ /postg/i # PostgreSQL, PostGIS
|
326
|
-
end
|
327
|
-
|
328
|
-
def postgresql_version
|
329
|
-
@postgresql_version ||= begin
|
330
|
-
target_version(StrongMigrations.target_postgresql_version) do
|
331
|
-
# only works with major versions
|
332
|
-
connection.select_all("SHOW server_version_num").first["server_version_num"].to_i / 10000
|
121
|
+
min_version = adapter.min_version
|
122
|
+
if min_version
|
123
|
+
version = adapter.server_version
|
124
|
+
if version < Gem::Version.new(min_version)
|
125
|
+
raise UnsupportedVersion, "#{adapter.name} version (#{version}) not supported in this version of Strong Migrations (#{StrongMigrations::VERSION})"
|
333
126
|
end
|
334
127
|
end
|
335
|
-
end
|
336
128
|
|
337
|
-
|
338
|
-
connection.adapter_name =~ /mysql/i && !connection.try(:mariadb?)
|
339
|
-
end
|
340
|
-
|
341
|
-
def mysql_version
|
342
|
-
@mysql_version ||= begin
|
343
|
-
target_version(StrongMigrations.target_mysql_version) do
|
344
|
-
connection.select_all("SELECT VERSION()").first["VERSION()"].split("-").first
|
345
|
-
end
|
346
|
-
end
|
129
|
+
@version_checked = true
|
347
130
|
end
|
348
131
|
|
349
|
-
def
|
350
|
-
|
351
|
-
end
|
132
|
+
def set_timeouts
|
133
|
+
return if @timeouts_set
|
352
134
|
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
135
|
+
if StrongMigrations.statement_timeout
|
136
|
+
adapter.set_statement_timeout(StrongMigrations.statement_timeout)
|
137
|
+
end
|
138
|
+
if StrongMigrations.lock_timeout
|
139
|
+
adapter.set_lock_timeout(StrongMigrations.lock_timeout)
|
358
140
|
end
|
359
|
-
end
|
360
141
|
|
361
|
-
|
362
|
-
version =
|
363
|
-
if target_version && StrongMigrations.developer_env?
|
364
|
-
target_version.to_s
|
365
|
-
else
|
366
|
-
yield
|
367
|
-
end
|
368
|
-
Gem::Version.new(version)
|
142
|
+
@timeouts_set = true
|
369
143
|
end
|
370
144
|
|
371
145
|
def check_lock_timeout
|
372
|
-
|
146
|
+
return if defined?(@lock_timeout_checked)
|
373
147
|
|
374
|
-
if
|
375
|
-
|
376
|
-
lock_timeout = connection.select_all("SHOW lock_timeout").first["lock_timeout"]
|
377
|
-
lock_timeout_sec = timeout_to_sec(lock_timeout)
|
378
|
-
if lock_timeout_sec == 0
|
379
|
-
warn "[strong_migrations] DANGER: No lock timeout set"
|
380
|
-
elsif lock_timeout_sec > limit
|
381
|
-
warn "[strong_migrations] DANGER: Lock timeout is longer than #{limit} seconds: #{lock_timeout}"
|
382
|
-
end
|
383
|
-
elsif mysql? || mariadb?
|
384
|
-
lock_timeout = connection.select_all("SHOW VARIABLES LIKE 'lock_wait_timeout'").first["Value"]
|
385
|
-
if lock_timeout.to_i > limit
|
386
|
-
warn "[strong_migrations] DANGER: Lock timeout is longer than #{limit} seconds: #{lock_timeout}"
|
387
|
-
end
|
388
|
-
end
|
389
|
-
@lock_timeout_checked = true
|
390
|
-
end
|
391
|
-
end
|
392
|
-
|
393
|
-
# units: https://www.postgresql.org/docs/current/config-setting.html
|
394
|
-
def timeout_to_sec(timeout)
|
395
|
-
units = {
|
396
|
-
"us" => 0.001,
|
397
|
-
"ms" => 1,
|
398
|
-
"s" => 1000,
|
399
|
-
"min" => 1000 * 60,
|
400
|
-
"h" => 1000 * 60 * 60,
|
401
|
-
"d" => 1000 * 60 * 60 * 24
|
402
|
-
}
|
403
|
-
timeout_ms = timeout.to_i
|
404
|
-
units.each do |k, v|
|
405
|
-
if timeout.end_with?(k)
|
406
|
-
timeout_ms *= v
|
407
|
-
break
|
408
|
-
end
|
148
|
+
if StrongMigrations.lock_timeout_limit
|
149
|
+
adapter.check_lock_timeout(StrongMigrations.lock_timeout_limit)
|
409
150
|
end
|
410
|
-
timeout_ms / 1000.0
|
411
|
-
end
|
412
151
|
|
413
|
-
|
414
|
-
if timeout.is_a?(String)
|
415
|
-
timeout
|
416
|
-
else
|
417
|
-
timeout.to_i * 1000
|
418
|
-
end
|
152
|
+
@lock_timeout_checked = true
|
419
153
|
end
|
420
154
|
|
421
|
-
def
|
422
|
-
|
423
|
-
SELECT conname AS name, pg_get_constraintdef(oid) AS def FROM pg_constraint
|
424
|
-
WHERE contype = 'c'
|
425
|
-
AND convalidated
|
426
|
-
AND conrelid = #{connection.quote(connection.quote_table_name(table_name))}::regclass
|
427
|
-
SQL
|
428
|
-
connection.select_all(query.squish).to_a
|
155
|
+
def safe?
|
156
|
+
@safe || ENV["SAFETY_ASSURED"] || (direction == :down && !StrongMigrations.check_down) || version_safe?
|
429
157
|
end
|
430
158
|
|
431
|
-
def
|
432
|
-
|
433
|
-
|
434
|
-
message = StrongMigrations.error_messages[message_key] || "Missing message"
|
435
|
-
message = message + append if append
|
436
|
-
|
437
|
-
vars[:migration_name] = @migration.class.name
|
438
|
-
vars[:migration_suffix] = "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
439
|
-
vars[:base_model] = "ApplicationRecord"
|
440
|
-
|
441
|
-
# escape % not followed by {
|
442
|
-
message = message.gsub(/%(?!{)/, "%%") % vars if message.include?("%")
|
443
|
-
@migration.stop!(message, header: header || "Dangerous operation detected")
|
159
|
+
def version_safe?
|
160
|
+
version && version <= StrongMigrations.start_after
|
444
161
|
end
|
445
162
|
|
446
|
-
def
|
447
|
-
|
448
|
-
code = statement % identifiers.map { |v| connection.quote_table_name(v) }
|
449
|
-
"safety_assured do\n execute '#{code}' \n end"
|
163
|
+
def version
|
164
|
+
@migration.version
|
450
165
|
end
|
451
166
|
|
452
|
-
def
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
# pretty index: {algorithm: :concurrently}
|
462
|
-
"#{k}: {#{v.map { |k2, v2| "#{k2}: #{v2.inspect}" }.join(", ")}}"
|
167
|
+
def adapter
|
168
|
+
@adapter ||= begin
|
169
|
+
cls =
|
170
|
+
case connection.adapter_name
|
171
|
+
when /postg/i # PostgreSQL, PostGIS
|
172
|
+
Adapters::PostgreSQLAdapter
|
173
|
+
when /mysql/i
|
174
|
+
if connection.try(:mariadb?)
|
175
|
+
Adapters::MariaDBAdapter
|
463
176
|
else
|
464
|
-
|
177
|
+
Adapters::MySQLAdapter
|
465
178
|
end
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
str_args << last_arg.inspect
|
470
|
-
end
|
179
|
+
else
|
180
|
+
Adapters::AbstractAdapter
|
181
|
+
end
|
471
182
|
|
472
|
-
|
183
|
+
cls.new(self)
|
184
|
+
end
|
473
185
|
end
|
474
186
|
|
475
|
-
def
|
476
|
-
|
477
|
-
"#{model}.unscoped.in_batches do |relation| \n relation.update_all #{column}: #{default.inspect}\n sleep(0.01)\n end"
|
187
|
+
def connection
|
188
|
+
@migration.connection
|
478
189
|
end
|
479
190
|
|
480
|
-
def
|
481
|
-
|
191
|
+
def retry_lock_timeouts?(method)
|
192
|
+
(
|
193
|
+
StrongMigrations.lock_timeout_retries > 0 &&
|
194
|
+
!in_transaction? &&
|
195
|
+
method != :transaction
|
196
|
+
)
|
482
197
|
end
|
483
198
|
end
|
484
199
|
end
|