strong_migrations 0.8.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,176 @@ 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
51
+ when :create_join_table
52
+ check_create_join_table(*args)
172
53
  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
54
+ check_create_table(*args)
213
55
  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
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
311
65
  when :validate_check_constraint
312
- if postgresql? && writes_blocked?
313
- raise_error :validate_check_constraint
314
- end
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
315
74
  end
316
75
 
76
+ # custom checks
317
77
  StrongMigrations.checks.each do |check|
318
78
  @migration.instance_exec(method, args, &check)
319
79
  end
320
80
  end
321
81
 
322
- result = yield
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
323
91
 
324
92
  # outdated statistics + a new index can hurt performance of existing queries
325
93
  if StrongMigrations.auto_analyze && direction == :up && method == :add_index
326
- analyze_table(args[0])
94
+ adapter.analyze_table(args[0])
327
95
  end
328
96
 
329
97
  result
330
98
  end
331
99
 
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)
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
363
111
  end
364
-
365
- @timeouts_set = true
112
+ raise e
366
113
  end
367
114
  end
368
115
 
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
116
+ private
384
117
 
385
- def postgresql?
386
- connection.adapter_name =~ /postg/i # PostgreSQL, PostGIS
387
- end
118
+ def check_version_supported
119
+ return if defined?(@version_checked)
388
120
 
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
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})"
394
126
  end
395
127
  end
396
- end
397
128
 
398
- def mysql?
399
- connection.adapter_name =~ /mysql/i && !connection.try(:mariadb?)
129
+ @version_checked = true
400
130
  end
401
131
 
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
132
+ def set_timeouts
133
+ return if @timeouts_set
413
134
 
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
135
+ if StrongMigrations.statement_timeout
136
+ adapter.set_statement_timeout(StrongMigrations.statement_timeout)
419
137
  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
138
+ if StrongMigrations.lock_timeout
139
+ adapter.set_lock_timeout(StrongMigrations.lock_timeout)
457
140
  end
458
- end
459
141
 
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
142
+ @timeouts_set = true
478
143
  end
479
144
 
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
145
+ def check_lock_timeout
146
+ return if defined?(@lock_timeout_checked)
488
147
 
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)}"
148
+ if StrongMigrations.lock_timeout_limit
149
+ adapter.check_lock_timeout(StrongMigrations.lock_timeout_limit)
494
150
  end
495
- end
496
151
 
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
152
+ @lock_timeout_checked = true
510
153
  end
511
154
 
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")
155
+ def safe?
156
+ @safe || ENV["SAFETY_ASSURED"] || (direction == :down && !StrongMigrations.check_down) || version_safe?
525
157
  end
526
158
 
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) }
159
+ def version_safe?
160
+ version && version <= StrongMigrations.start_after
530
161
  end
531
162
 
532
- def safety_assured_str(code)
533
- "safety_assured do\n execute '#{code}' \n end"
163
+ def version
164
+ @migration.version
534
165
  end
535
166
 
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(", ")}}"
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
547
176
  else
548
- "#{k}: #{v.inspect}"
177
+ Adapters::MySQLAdapter
549
178
  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
179
+ else
180
+ Adapters::AbstractAdapter
181
+ end
575
182
 
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"
183
+ cls.new(self)
184
+ end
579
185
  end
580
186
 
581
- def new_table?(table)
582
- @new_tables.include?(table.to_s)
187
+ def connection
188
+ @migration.connection
583
189
  end
584
190
 
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
191
+ def retry_lock_timeouts?(method)
192
+ (
193
+ StrongMigrations.lock_timeout_retries > 0 &&
194
+ !in_transaction? &&
195
+ method != :transaction
196
+ )
595
197
  end
596
198
  end
597
199
  end