strong_migrations 0.8.0 → 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.
@@ -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