strong_migrations 0.8.0 → 1.0.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.
@@ -1,15 +1,16 @@
1
1
  module StrongMigrations
2
2
  class Checker
3
+ include Checks
3
4
  include SafeMethods
4
5
 
5
- attr_accessor :direction, :transaction_disabled
6
+ attr_accessor :direction, :transaction_disabled, :timeouts_set
6
7
 
7
8
  def initialize(migration)
8
9
  @migration = migration
9
10
  @new_tables = []
10
11
  @safe = false
11
12
  @timeouts_set = false
12
- @lock_timeout_checked = false
13
+ @committed = false
13
14
  end
14
15
 
15
16
  def safety_assured
@@ -23,575 +24,174 @@ module StrongMigrations
23
24
  end
24
25
 
25
26
  def perform(method, *args)
27
+ check_version_supported
26
28
  set_timeouts
27
29
  check_lock_timeout
28
30
 
29
31
  if !safe? || safe_by_default_method?(method)
32
+ # TODO better pattern
33
+ # see checks.rb for methods
30
34
  case method
31
- when :remove_column, :remove_columns, :remove_timestamps, :remove_reference, :remove_belongs_to
32
- columns =
33
- case method
34
- when :remove_timestamps
35
- ["created_at", "updated_at"]
36
- when :remove_column
37
- [args[1].to_s]
38
- when :remove_columns
39
- args[1..-1].map(&:to_s)
40
- else
41
- options = args[2] || {}
42
- reference = args[1]
43
- cols = []
44
- cols << "#{reference}_type" if options[:polymorphic]
45
- cols << "#{reference}_id"
46
- cols
47
- end
48
-
49
- code = "self.ignored_columns = #{columns.inspect}"
50
-
51
- raise_error :remove_column,
52
- model: args[0].to_s.classify,
53
- code: code,
54
- command: command_str(method, args),
55
- column_suffix: columns.size > 1 ? "s" : ""
56
- when :change_table
57
- raise_error :change_table, header: "Possibly dangerous operation"
58
- when :rename_table
59
- raise_error :rename_table
60
- when :rename_column
61
- raise_error :rename_column
62
- when :add_index
63
- table, columns, options = args
64
- options ||= {}
65
-
66
- if columns.is_a?(Array) && columns.size > 3 && !options[:unique]
67
- raise_error :add_index_columns, header: "Best practice"
68
- end
69
- if postgresql? && options[:algorithm] != :concurrently && !new_table?(table)
70
- return safe_add_index(table, columns, options) if StrongMigrations.safe_by_default
71
- raise_error :add_index, command: command_str("add_index", [table, columns, options.merge(algorithm: :concurrently)])
72
- end
73
- when :remove_index
74
- table, options = args
75
- unless options.is_a?(Hash)
76
- options = {column: options}
77
- end
78
- options ||= {}
79
-
80
- if postgresql? && options[:algorithm] != :concurrently && !new_table?(table)
81
- return safe_remove_index(table, options) if StrongMigrations.safe_by_default
82
- raise_error :remove_index, command: command_str("remove_index", [table, options.merge(algorithm: :concurrently)])
83
- end
35
+ when :add_check_constraint
36
+ check_add_check_constraint(args)
84
37
  when :add_column
85
- table, column, type, options = args
86
- options ||= {}
87
- default = options[:default]
88
-
89
- if !default.nil? && !add_column_default_safe?
90
- if options[:null] == false
91
- options = options.except(:null)
92
- append = "
93
-
94
- Then add the NOT NULL constraint in separate migrations."
95
- end
96
-
97
- raise_error :add_column_default,
98
- add_command: command_str("add_column", [table, column, type, options.except(:default)]),
99
- change_command: command_str("change_column_default", [table, column, default]),
100
- remove_command: command_str("remove_column", [table, column]),
101
- code: backfill_code(table, column, default),
102
- append: append,
103
- rewrite_blocks: rewrite_blocks
104
- end
105
-
106
- if type.to_s == "json" && postgresql?
107
- raise_error :add_column_json,
108
- command: command_str("add_column", [table, column, :jsonb, options])
109
- 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)
110
45
  when :change_column
111
- table, column, type, options = args
112
- options ||= {}
113
-
114
- safe = false
115
- existing_column = connection.columns(table).find { |c| c.name.to_s == column.to_s }
116
- if existing_column
117
- existing_type = existing_column.sql_type.split("(").first
118
- if postgresql?
119
- case type.to_s
120
- when "string"
121
- # safe to increase limit or remove it
122
- # not safe to decrease limit or add a limit
123
- case existing_type
124
- when "character varying"
125
- safe = !options[:limit] || (existing_column.limit && options[:limit] >= existing_column.limit)
126
- when "text"
127
- safe = !options[:limit]
128
- end
129
- when "text"
130
- # safe to change varchar to text (and text to text)
131
- safe = ["character varying", "text"].include?(existing_type)
132
- when "numeric", "decimal"
133
- # numeric and decimal are equivalent and can be used interchangably
134
- safe = ["numeric", "decimal"].include?(existing_type) &&
135
- (
136
- (
137
- # unconstrained
138
- !options[:precision] && !options[:scale]
139
- ) || (
140
- # increased precision, same scale
141
- options[:precision] && existing_column.precision &&
142
- options[:precision] >= existing_column.precision &&
143
- options[:scale] == existing_column.scale
144
- )
145
- )
146
- when "datetime", "timestamp", "timestamptz"
147
- safe = ["timestamp without time zone", "timestamp with time zone"].include?(existing_type) &&
148
- postgresql_version >= Gem::Version.new("12") &&
149
- connection.select_all("SHOW timezone").first["TimeZone"] == "UTC"
150
- end
151
- elsif mysql? || mariadb?
152
- case type.to_s
153
- when "string"
154
- # https://dev.mysql.com/doc/refman/5.7/en/innodb-online-ddl-operations.html
155
- # https://mariadb.com/kb/en/innodb-online-ddl-operations-with-the-instant-alter-algorithm/#changing-the-data-type-of-a-column
156
- # increased limit, but doesn't change number of length bytes
157
- # 1-255 = 1 byte, 256-65532 = 2 bytes, 65533+ = too big for varchar
158
- limit = options[:limit] || 255
159
- safe = ["varchar"].include?(existing_type) &&
160
- limit >= existing_column.limit &&
161
- (limit <= 255 || existing_column.limit > 255)
162
- end
163
- end
164
- end
165
-
166
- # unsafe to set NOT NULL for safe types
167
- if safe && existing_column.null && options[:null] == false
168
- raise_error :change_column_with_not_null
169
- end
170
-
171
- raise_error :change_column, rewrite_blocks: rewrite_blocks 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
172
51
  when :create_table
173
- table, options = args
174
- options ||= {}
175
-
176
- raise_error :create_table if options[:force]
177
-
178
- # keep track of new tables of add_index check
179
- @new_tables << table.to_s
180
- when :add_reference, :add_belongs_to
181
- table, reference, options = args
182
- options ||= {}
183
-
184
- if postgresql?
185
- index_value = options.fetch(:index, true)
186
- concurrently_set = index_value.is_a?(Hash) && index_value[:algorithm] == :concurrently
187
- bad_index = index_value && !concurrently_set
188
-
189
- if bad_index || options[:foreign_key]
190
- if index_value.is_a?(Hash)
191
- options[:index] = options[:index].merge(algorithm: :concurrently)
192
- else
193
- options = options.merge(index: {algorithm: :concurrently})
194
- end
195
-
196
- return safe_add_reference(table, reference, options) if StrongMigrations.safe_by_default
197
-
198
- if options.delete(:foreign_key)
199
- headline = "Adding a foreign key blocks writes on both tables."
200
- append = "
201
-
202
- Then add the foreign key in separate migrations."
203
- else
204
- headline = "Adding an index non-concurrently locks the table."
205
- end
206
-
207
- raise_error :add_reference,
208
- headline: headline,
209
- command: command_str(method, [table, reference, options]),
210
- append: append
211
- end
212
- end
52
+ check_create_table(args)
213
53
  when :execute
214
- raise_error :execute, header: "Possibly dangerous operation"
215
- when :change_column_null
216
- table, column, null, default = args
217
- if !null
218
- if postgresql?
219
- safe = false
220
- if postgresql_version >= Gem::Version.new("12")
221
- safe = constraints(table).any? { |c| c["def"] == "CHECK ((#{column} IS NOT NULL))" || c["def"] == "CHECK ((#{connection.quote_column_name(column)} IS NOT NULL))" }
222
- end
223
-
224
- unless safe
225
- # match https://github.com/nullobject/rein
226
- constraint_name = "#{table}_#{column}_null"
227
-
228
- add_code = constraint_str("ALTER TABLE %s ADD CONSTRAINT %s CHECK (%s IS NOT NULL) NOT VALID", [table, constraint_name, column])
229
- validate_code = constraint_str("ALTER TABLE %s VALIDATE CONSTRAINT %s", [table, constraint_name])
230
- remove_code = constraint_str("ALTER TABLE %s DROP CONSTRAINT %s", [table, constraint_name])
231
-
232
- constraint_methods = ar_version >= 6.1
233
-
234
- validate_constraint_code =
235
- if constraint_methods
236
- String.new(command_str(:validate_check_constraint, [table, {name: constraint_name}]))
237
- else
238
- String.new(safety_assured_str(validate_code))
239
- end
240
-
241
- if postgresql_version >= Gem::Version.new("12")
242
- change_args = [table, column, null]
243
-
244
- validate_constraint_code << "\n #{command_str(:change_column_null, change_args)}"
245
-
246
- if constraint_methods
247
- validate_constraint_code << "\n #{command_str(:remove_check_constraint, [table, {name: constraint_name}])}"
248
- else
249
- validate_constraint_code << "\n #{safety_assured_str(remove_code)}"
250
- end
251
- end
252
-
253
- return safe_change_column_null(add_code, validate_code, change_args, remove_code) if StrongMigrations.safe_by_default
254
-
255
- add_constraint_code =
256
- if constraint_methods
257
- # only quote when needed
258
- expr_column = column.to_s =~ /\A[a-z0-9_]+\z/ ? column : connection.quote_column_name(column)
259
- command_str(:add_check_constraint, [table, "#{expr_column} IS NOT NULL", {name: constraint_name, validate: false}])
260
- else
261
- safety_assured_str(add_code)
262
- end
263
-
264
- raise_error :change_column_null_postgresql,
265
- add_constraint_code: add_constraint_code,
266
- validate_constraint_code: validate_constraint_code
267
- end
268
- elsif mysql? || mariadb?
269
- raise_error :change_column_null_mysql
270
- elsif !default.nil?
271
- raise_error :change_column_null,
272
- code: backfill_code(table, column, default)
273
- end
274
- end
275
- when :add_foreign_key
276
- from_table, to_table, options = args
277
- options ||= {}
278
-
279
- validate = options.fetch(:validate, true)
280
-
281
- if postgresql? && validate
282
- return safe_add_foreign_key(from_table, to_table, options) if StrongMigrations.safe_by_default
283
-
284
- raise_error :add_foreign_key,
285
- add_foreign_key_code: command_str("add_foreign_key", [from_table, to_table, options.merge(validate: false)]),
286
- validate_foreign_key_code: command_str("validate_foreign_key", [from_table, to_table])
287
- end
288
- when :validate_foreign_key
289
- if postgresql? && writes_blocked?
290
- raise_error :validate_foreign_key
291
- end
292
- when :add_check_constraint
293
- table, expression, options = args
294
- options ||= {}
295
-
296
- if !new_table?(table)
297
- if postgresql? && options[:validate] != false
298
- add_options = options.merge(validate: false)
299
- name = options[:name] || @migration.check_constraint_options(table, expression, options)[:name]
300
- validate_options = {name: name}
301
-
302
- return safe_add_check_constraint(table, expression, add_options, validate_options) if StrongMigrations.safe_by_default
303
-
304
- raise_error :add_check_constraint,
305
- add_check_constraint_code: command_str("add_check_constraint", [table, expression, add_options]),
306
- validate_check_constraint_code: command_str("validate_check_constraint", [table, validate_options])
307
- elsif mysql? || mariadb?
308
- raise_error :add_check_constraint_mysql
309
- end
310
- end
54
+ check_execute
55
+ when :remove_column, :remove_columns, :remove_timestamps, :remove_reference, :remove_belongs_to
56
+ check_remove_column(method, args)
57
+ when :remove_index
58
+ check_remove_index(args)
59
+ when :rename_column
60
+ check_rename_column
61
+ when :rename_table
62
+ check_rename_table
311
63
  when :validate_check_constraint
312
- if postgresql? && writes_blocked?
313
- raise_error :validate_check_constraint
314
- end
64
+ check_validate_check_constraint
65
+ when :validate_foreign_key
66
+ check_validate_foreign_key
67
+ when :commit_db_transaction
68
+ # if committed, likely no longer in DDL transaction
69
+ # and no longer eligible to be retried at migration level
70
+ # okay to have false positives
71
+ @committed = true
315
72
  end
316
73
 
74
+ # custom checks
317
75
  StrongMigrations.checks.each do |check|
318
76
  @migration.instance_exec(method, args, &check)
319
77
  end
320
78
  end
321
79
 
322
- result = yield
80
+ result =
81
+ if retry_lock_timeouts?(method)
82
+ # TODO figure out how to handle methods that generate multiple statements
83
+ # like add_reference(table, ref, index: {algorithm: :concurrently})
84
+ # lock timeout after first statement will cause retry to fail
85
+ retry_lock_timeouts { yield }
86
+ else
87
+ yield
88
+ end
323
89
 
324
90
  # outdated statistics + a new index can hurt performance of existing queries
325
91
  if StrongMigrations.auto_analyze && direction == :up && method == :add_index
326
- analyze_table(args[0])
92
+ adapter.analyze_table(args[0])
327
93
  end
328
94
 
329
95
  result
330
96
  end
331
97
 
332
- private
333
-
334
- def set_timeouts
335
- if !@timeouts_set
336
- if StrongMigrations.statement_timeout
337
- statement =
338
- if postgresql?
339
- "SET statement_timeout TO #{connection.quote(postgresql_timeout(StrongMigrations.statement_timeout))}"
340
- elsif mysql?
341
- # use ceil to prevent no timeout for values under 1 ms
342
- "SET max_execution_time = #{connection.quote((StrongMigrations.statement_timeout.to_f * 1000).ceil)}"
343
- elsif mariadb?
344
- "SET max_statement_time = #{connection.quote(StrongMigrations.statement_timeout)}"
345
- else
346
- raise StrongMigrations::Error, "Statement timeout not supported for this database"
347
- end
348
-
349
- connection.select_all(statement)
350
- end
351
-
352
- if StrongMigrations.lock_timeout
353
- statement =
354
- if postgresql?
355
- "SET lock_timeout TO #{connection.quote(postgresql_timeout(StrongMigrations.lock_timeout))}"
356
- elsif mysql? || mariadb?
357
- "SET lock_wait_timeout = #{connection.quote(StrongMigrations.lock_timeout)}"
358
- else
359
- raise StrongMigrations::Error, "Lock timeout not supported for this database"
360
- end
361
-
362
- connection.select_all(statement)
98
+ def retry_lock_timeouts(check_committed: false)
99
+ retries = 0
100
+ begin
101
+ yield
102
+ rescue ActiveRecord::LockWaitTimeout => e
103
+ if retries < StrongMigrations.lock_timeout_retries && !(check_committed && @committed)
104
+ retries += 1
105
+ delay = StrongMigrations.lock_timeout_retry_delay
106
+ @migration.say("Lock timeout. Retrying in #{delay} seconds...")
107
+ sleep(delay)
108
+ retry
363
109
  end
364
-
365
- @timeouts_set = true
110
+ raise e
366
111
  end
367
112
  end
368
113
 
369
- def connection
370
- @migration.connection
371
- end
372
-
373
- def version
374
- @migration.version
375
- end
376
-
377
- def safe?
378
- @safe || ENV["SAFETY_ASSURED"] || (direction == :down && !StrongMigrations.check_down) || version_safe?
379
- end
380
-
381
- def version_safe?
382
- version && version <= StrongMigrations.start_after
383
- end
114
+ private
384
115
 
385
- def postgresql?
386
- connection.adapter_name =~ /postg/i # PostgreSQL, PostGIS
387
- end
116
+ def check_version_supported
117
+ return if defined?(@version_checked)
388
118
 
389
- def postgresql_version
390
- @postgresql_version ||= begin
391
- target_version(StrongMigrations.target_postgresql_version) do
392
- # only works with major versions
393
- connection.select_all("SHOW server_version_num").first["server_version_num"].to_i / 10000
119
+ min_version = adapter.min_version
120
+ if min_version
121
+ version = adapter.server_version
122
+ if version < Gem::Version.new(min_version)
123
+ raise UnsupportedVersion, "#{adapter.name} version (#{version}) not supported in this version of Strong Migrations (#{StrongMigrations::VERSION})"
394
124
  end
395
125
  end
396
- end
397
126
 
398
- def mysql?
399
- connection.adapter_name =~ /mysql/i && !connection.try(:mariadb?)
127
+ @version_checked = true
400
128
  end
401
129
 
402
- def mysql_version
403
- @mysql_version ||= begin
404
- target_version(StrongMigrations.target_mysql_version) do
405
- connection.select_all("SELECT VERSION()").first["VERSION()"].split("-").first
406
- end
407
- end
408
- end
409
-
410
- def mariadb?
411
- connection.adapter_name =~ /mysql/i && connection.try(:mariadb?)
412
- end
130
+ def set_timeouts
131
+ return if @timeouts_set
413
132
 
414
- def mariadb_version
415
- @mariadb_version ||= begin
416
- target_version(StrongMigrations.target_mariadb_version) do
417
- connection.select_all("SELECT VERSION()").first["VERSION()"].split("-").first
418
- end
133
+ if StrongMigrations.statement_timeout
134
+ adapter.set_statement_timeout(StrongMigrations.statement_timeout)
419
135
  end
420
- end
421
-
422
- def target_version(target_version)
423
- target_version ||= StrongMigrations.target_version
424
- version =
425
- if target_version && StrongMigrations.developer_env?
426
- target_version.to_s
427
- else
428
- yield
429
- end
430
- Gem::Version.new(version)
431
- end
432
-
433
- def ar_version
434
- ActiveRecord::VERSION::STRING.to_f
435
- end
436
-
437
- def check_lock_timeout
438
- limit = StrongMigrations.lock_timeout_limit
439
-
440
- if limit && !@lock_timeout_checked
441
- if postgresql?
442
- lock_timeout = connection.select_all("SHOW lock_timeout").first["lock_timeout"]
443
- lock_timeout_sec = timeout_to_sec(lock_timeout)
444
- if lock_timeout_sec == 0
445
- warn "[strong_migrations] DANGER: No lock timeout set"
446
- elsif lock_timeout_sec > limit
447
- warn "[strong_migrations] DANGER: Lock timeout is longer than #{limit} seconds: #{lock_timeout}"
448
- end
449
- elsif mysql? || mariadb?
450
- lock_timeout = connection.select_all("SHOW VARIABLES LIKE 'lock_wait_timeout'").first["Value"]
451
- # lock timeout is an integer
452
- if lock_timeout.to_i > limit
453
- warn "[strong_migrations] DANGER: Lock timeout is longer than #{limit} seconds: #{lock_timeout}"
454
- end
455
- end
456
- @lock_timeout_checked = true
136
+ if StrongMigrations.lock_timeout
137
+ adapter.set_lock_timeout(StrongMigrations.lock_timeout)
457
138
  end
458
- end
459
139
 
460
- # units: https://www.postgresql.org/docs/current/config-setting.html
461
- def timeout_to_sec(timeout)
462
- units = {
463
- "us" => 0.001,
464
- "ms" => 1,
465
- "s" => 1000,
466
- "min" => 1000 * 60,
467
- "h" => 1000 * 60 * 60,
468
- "d" => 1000 * 60 * 60 * 24
469
- }
470
- timeout_ms = timeout.to_i
471
- units.each do |k, v|
472
- if timeout.end_with?(k)
473
- timeout_ms *= v
474
- break
475
- end
476
- end
477
- timeout_ms / 1000.0
140
+ @timeouts_set = true
478
141
  end
479
142
 
480
- def postgresql_timeout(timeout)
481
- if timeout.is_a?(String)
482
- timeout
483
- else
484
- # use ceil to prevent no timeout for values under 1 ms
485
- (timeout.to_f * 1000).ceil
486
- end
487
- end
143
+ def check_lock_timeout
144
+ return if defined?(@lock_timeout_checked)
488
145
 
489
- def analyze_table(table)
490
- if postgresql?
491
- connection.execute "ANALYZE #{connection.quote_table_name(table.to_s)}"
492
- elsif mariadb? || mysql?
493
- connection.execute "ANALYZE TABLE #{connection.quote_table_name(table.to_s)}"
146
+ if StrongMigrations.lock_timeout_limit
147
+ adapter.check_lock_timeout(StrongMigrations.lock_timeout_limit)
494
148
  end
495
- end
496
149
 
497
- def constraints(table_name)
498
- query = <<~SQL
499
- SELECT
500
- conname AS name,
501
- pg_get_constraintdef(oid) AS def
502
- FROM
503
- pg_constraint
504
- WHERE
505
- contype = 'c' AND
506
- convalidated AND
507
- conrelid = #{connection.quote(connection.quote_table_name(table_name))}::regclass
508
- SQL
509
- connection.select_all(query.squish).to_a
150
+ @lock_timeout_checked = true
510
151
  end
511
152
 
512
- def raise_error(message_key, header: nil, append: nil, **vars)
513
- return unless StrongMigrations.check_enabled?(message_key, version: version)
514
-
515
- message = StrongMigrations.error_messages[message_key] || "Missing message"
516
- message = message + append if append
517
-
518
- vars[:migration_name] = @migration.class.name
519
- vars[:migration_suffix] = "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
520
- vars[:base_model] = "ApplicationRecord"
521
-
522
- # escape % not followed by {
523
- message = message.gsub(/%(?!{)/, "%%") % vars if message.include?("%")
524
- @migration.stop!(message, header: header || "Dangerous operation detected")
153
+ def safe?
154
+ @safe || ENV["SAFETY_ASSURED"] || (direction == :down && !StrongMigrations.check_down) || version_safe?
525
155
  end
526
156
 
527
- def constraint_str(statement, identifiers)
528
- # not all identifiers are tables, but this method of quoting should be fine
529
- statement % identifiers.map { |v| connection.quote_table_name(v) }
157
+ def version_safe?
158
+ version && version <= StrongMigrations.start_after
530
159
  end
531
160
 
532
- def safety_assured_str(code)
533
- "safety_assured do\n execute '#{code}' \n end"
161
+ def version
162
+ @migration.version
534
163
  end
535
164
 
536
- def command_str(command, args)
537
- str_args = args[0..-2].map { |a| a.inspect }
538
-
539
- # prettier last arg
540
- last_arg = args[-1]
541
- if last_arg.is_a?(Hash)
542
- if last_arg.any?
543
- str_args << last_arg.map do |k, v|
544
- if v.is_a?(Hash)
545
- # pretty index: {algorithm: :concurrently}
546
- "#{k}: {#{v.map { |k2, v2| "#{k2}: #{v2.inspect}" }.join(", ")}}"
165
+ def adapter
166
+ @adapter ||= begin
167
+ cls =
168
+ case connection.adapter_name
169
+ when /postg/i # PostgreSQL, PostGIS
170
+ Adapters::PostgreSQLAdapter
171
+ when /mysql/i
172
+ if connection.try(:mariadb?)
173
+ Adapters::MariaDBAdapter
547
174
  else
548
- "#{k}: #{v.inspect}"
175
+ Adapters::MySQLAdapter
549
176
  end
550
- end.join(", ")
551
- end
552
- else
553
- str_args << last_arg.inspect
554
- end
555
-
556
- "#{command} #{str_args.join(", ")}"
557
- end
558
-
559
- def writes_blocked?
560
- query = <<~SQL
561
- SELECT
562
- relation::regclass::text
563
- FROM
564
- pg_locks
565
- WHERE
566
- mode IN ('ShareRowExclusiveLock', 'AccessExclusiveLock') AND
567
- pid = pg_backend_pid()
568
- SQL
569
- connection.select_all(query.squish).any?
570
- end
571
-
572
- def rewrite_blocks
573
- mysql? || mariadb? ? "writes" : "reads and writes"
574
- end
177
+ else
178
+ Adapters::AbstractAdapter
179
+ end
575
180
 
576
- def backfill_code(table, column, default)
577
- model = table.to_s.classify
578
- "#{model}.unscoped.in_batches do |relation| \n relation.update_all #{column}: #{default.inspect}\n sleep(0.01)\n end"
181
+ cls.new(self)
182
+ end
579
183
  end
580
184
 
581
- def new_table?(table)
582
- @new_tables.include?(table.to_s)
185
+ def connection
186
+ @migration.connection
583
187
  end
584
188
 
585
- def add_column_default_safe?
586
- if postgresql?
587
- postgresql_version >= Gem::Version.new("11")
588
- elsif mysql?
589
- mysql_version >= Gem::Version.new("8.0.12")
590
- elsif mariadb?
591
- mariadb_version >= Gem::Version.new("10.3.2")
592
- else
593
- false
594
- end
189
+ def retry_lock_timeouts?(method)
190
+ (
191
+ StrongMigrations.lock_timeout_retries > 0 &&
192
+ !in_transaction? &&
193
+ method != :transaction
194
+ )
595
195
  end
596
196
  end
597
197
  end