strong_migrations 2.0.2 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 98ed2c92ee055824eefbb8239b65a6ef1c847b1ffa87e8cafb7fce34f8878c3c
4
- data.tar.gz: 4f20acd9b50d07dfbe8ff6745f2fda6aaf7ca6731be72c8275929e15b12bd212
3
+ metadata.gz: c90bd33b534a7c37e59499e81b9c61e7d67a45bc849fd9dfe4d58285631438bf
4
+ data.tar.gz: 3771261014091caf3c28369db3ae4f97a1434d92b3207b351bf92c839caaec70
5
5
  SHA512:
6
- metadata.gz: 492d1dec59fc74196c3670b8a49fcc080af36eb3297c1c9aabfea7f0ff1943c21a4fe43e66d0e406d4eae1b785223f3e137bf05ee377d5597e87573f7b7c7af4
7
- data.tar.gz: 2a955f9eb53386c39678f48291603345be5d086877ce9a2fdd2f60dcc5eec84b671945fd9dba6a4f5d98d9d41da7cb32ab061daa333f461344ae7b9c7884c9d0
6
+ metadata.gz: aaa494cbbf0545df8795a5243d80f150e3be939507ab9379c20eb8b7cd68d5fde29c86cce63fffb005e3cd6ff5c858d084eb335bc7c911cccb4da751cfbbac32
7
+ data.tar.gz: b4833b425a23328795f14a6bcd94a4d06e11da5272976dfbeaaebcc469454b685058bc65c109561c513bc2434acf9119ecbae8b135b3c6d020d5ada607e86a38
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 2.1.0 (2024-11-08)
2
+
3
+ - Added `skip_database` method
4
+ - Added experimental `remove_invalid_indexes` option
5
+ - Added warning for unsupported adapters
6
+ - Improved output for `db:forward`, `db:rollback`, `db:migrate:up`, and `db:migrate:down`
7
+ - Made operations more retriable with `safe_by_default`
8
+
1
9
  ## 2.0.2 (2024-10-30)
2
10
 
3
11
  - Fixed migrations not running with Active Record 8 rc2
data/README.md CHANGED
@@ -351,6 +351,8 @@ end
351
351
 
352
352
  ### Backfilling data
353
353
 
354
+ Note: Strong Migrations does not detect dangerous backfills.
355
+
354
356
  #### Bad
355
357
 
356
358
  Active Record creates a transaction around each migration, and backfilling in the same transaction that alters a table keeps the table locked for the [duration of the backfill](https://wework.github.io/data/2015/11/05/add-columns-with-default-values-to-large-tables-in-rails-postgres/).
@@ -383,6 +385,8 @@ class BackfillSomeColumn < ActiveRecord::Migration[7.2]
383
385
  end
384
386
  ```
385
387
 
388
+ Note: If backfilling with a method other than `update_all`, use `User.reset_column_information` to ensure the model has up-to-date column information.
389
+
386
390
  ### Adding an index non-concurrently
387
391
 
388
392
  :turtle: Safe by default available
@@ -608,11 +612,16 @@ Then validate it in a separate migration. Once the check constraint is validated
608
612
 
609
613
  ```ruby
610
614
  class ValidateSomeColumnNotNull < ActiveRecord::Migration[7.2]
611
- def change
615
+ def up
612
616
  validate_check_constraint :users, name: "users_some_column_null"
613
617
  change_column_null :users, :some_column, false
614
618
  remove_check_constraint :users, name: "users_some_column_null"
615
619
  end
620
+
621
+ def down
622
+ add_check_constraint :users, "some_column IS NOT NULL", name: "users_some_column_null", validate: false
623
+ change_column_null :users, :some_column, true
624
+ end
616
625
  end
617
626
  ```
618
627
 
@@ -723,7 +732,7 @@ Certain methods like `execute` and `change_table` cannot be inspected and are pr
723
732
 
724
733
  ## Safe by Default
725
734
 
726
- Make operations safe by default.
735
+ Make certain operations safe by default. This allows you to write the code under the "Bad" section, but the migration will be performed as if you had written the "Good" version.
727
736
 
728
737
  - adding and removing an index
729
738
  - adding a foreign key
@@ -772,6 +781,16 @@ StrongMigrations.disable_check(:add_index)
772
781
 
773
782
  Check the [source code](https://github.com/ankane/strong_migrations/blob/master/lib/strong_migrations/error_messages.rb) for the list of keys.
774
783
 
784
+ ## Skip Databases
785
+
786
+ Skip checks and other functionality for specific databases with:
787
+
788
+ ```ruby
789
+ StrongMigrations.skip_database(:catalog)
790
+ ```
791
+
792
+ Note: This does not affect `alphabetize_schema`.
793
+
775
794
  ## Down Migrations / Rollbacks
776
795
 
777
796
  By default, checks are disabled when migrating down. Enable them with:
@@ -853,7 +872,21 @@ production:
853
872
 
854
873
  For HTTP connections, Redis, and other services, check out [this guide](https://github.com/ankane/the-ultimate-guide-to-ruby-timeouts).
855
874
 
856
- ## Lock Timeout Retries [experimental]
875
+ ## Invalid Indexes
876
+
877
+ In Postgres, adding an index non-concurrently can leave behind an invalid index if the lock timeout is reached. Running the migration again can result in an error.
878
+
879
+ To automatically remove the invalid index when the migration runs again, use:
880
+
881
+ ```ruby
882
+ StrongMigrations.remove_invalid_indexes = true
883
+ ```
884
+
885
+ Note: This feature is experimental.
886
+
887
+ ## Lock Timeout Retries
888
+
889
+ Note: This feature is experimental.
857
890
 
858
891
  There’s the option to automatically retry statements for migrations when the lock timeout is reached. Here’s how it works:
859
892
 
@@ -13,11 +13,11 @@ module StrongMigrations
13
13
  end
14
14
 
15
15
  def set_statement_timeout(timeout)
16
- raise StrongMigrations::Error, "Statement timeout not supported for this database"
16
+ # do nothing
17
17
  end
18
18
 
19
19
  def set_lock_timeout(timeout)
20
- raise StrongMigrations::Error, "Lock timeout not supported for this database"
20
+ # do nothing
21
21
  end
22
22
 
23
23
  def check_lock_timeout(limit)
@@ -127,19 +127,19 @@ module StrongMigrations
127
127
  safe
128
128
  end
129
129
 
130
- def constraints(table_name)
130
+ # TODO remove when Active Record < 7.1 is no longer supported
131
+ def index_invalid?(table_name, index_name)
131
132
  query = <<~SQL
132
133
  SELECT
133
- conname AS name,
134
- pg_get_constraintdef(oid) AS def
134
+ indisvalid
135
135
  FROM
136
- pg_constraint
136
+ pg_index
137
137
  WHERE
138
- contype = 'c' AND
139
- convalidated AND
140
- conrelid = #{connection.quote(connection.quote_table_name(table_name))}::regclass
138
+ indrelid = to_regclass(#{connection.quote(connection.quote_table_name(table_name))}) AND
139
+ indexrelid = to_regclass(#{connection.quote(connection.quote_table_name(index_name))}) AND
140
+ indisvalid = false
141
141
  SQL
142
- select_all(query.squish).to_a
142
+ select_all(query.squish).any?
143
143
  end
144
144
 
145
145
  def writes_blocked?
@@ -11,10 +11,16 @@ module StrongMigrations
11
11
 
12
12
  def initialize(migration)
13
13
  @migration = migration
14
+ reset
15
+ end
16
+
17
+ def reset
14
18
  @new_tables = []
15
19
  @new_columns = []
16
20
  @timeouts_set = false
17
21
  @committed = false
22
+ @transaction_disabled = false
23
+ @skip_retries = false
18
24
  end
19
25
 
20
26
  def self.safety_assured
@@ -27,7 +33,10 @@ module StrongMigrations
27
33
  end
28
34
  end
29
35
 
30
- def perform(method, *args)
36
+ def perform(method, *args, &block)
37
+ return yield if skip?
38
+
39
+ check_adapter
31
40
  check_version_supported
32
41
  set_timeouts
33
42
  check_lock_timeout
@@ -96,9 +105,9 @@ module StrongMigrations
96
105
  # TODO figure out how to handle methods that generate multiple statements
97
106
  # like add_reference(table, ref, index: {algorithm: :concurrently})
98
107
  # lock timeout after first statement will cause retry to fail
99
- retry_lock_timeouts { yield }
108
+ retry_lock_timeouts { perform_method(method, *args, &block) }
100
109
  else
101
- yield
110
+ perform_method(method, *args, &block)
102
111
  end
103
112
 
104
113
  # outdated statistics + a new index can hurt performance of existing queries
@@ -109,6 +118,13 @@ module StrongMigrations
109
118
  result
110
119
  end
111
120
 
121
+ def perform_method(method, *args)
122
+ if StrongMigrations.remove_invalid_indexes && direction == :up && method == :add_index && postgresql?
123
+ remove_invalid_index_if_needed(*args)
124
+ end
125
+ yield
126
+ end
127
+
112
128
  def retry_lock_timeouts(check_committed: false)
113
129
  retries = 0
114
130
  begin
@@ -129,8 +145,22 @@ module StrongMigrations
129
145
  version && version <= StrongMigrations.start_after
130
146
  end
131
147
 
148
+ def skip?
149
+ StrongMigrations.skipped_databases.map(&:to_s).include?(db_config_name)
150
+ end
151
+
132
152
  private
133
153
 
154
+ def check_adapter
155
+ return if defined?(@adapter_checked)
156
+
157
+ if adapter.instance_of?(Adapters::AbstractAdapter)
158
+ warn "[strong_migrations] Unsupported adapter: #{connection.adapter_name}. Use StrongMigrations.skip_database(#{db_config_name.to_sym.inspect}) to silence this warning."
159
+ end
160
+
161
+ @adapter_checked = true
162
+ end
163
+
134
164
  def check_version_supported
135
165
  return if defined?(@version_checked)
136
166
 
@@ -200,12 +230,48 @@ module StrongMigrations
200
230
  @migration.connection
201
231
  end
202
232
 
233
+ def db_config_name
234
+ connection.pool.db_config.name
235
+ end
236
+
203
237
  def retry_lock_timeouts?(method)
204
238
  (
205
239
  StrongMigrations.lock_timeout_retries > 0 &&
206
240
  !in_transaction? &&
207
- method != :transaction
241
+ method != :transaction &&
242
+ !@skip_retries
208
243
  )
209
244
  end
245
+
246
+ def without_retries
247
+ previous_value = @skip_retries
248
+ begin
249
+ @skip_retries = true
250
+ yield
251
+ ensure
252
+ @skip_retries = previous_value
253
+ end
254
+ end
255
+
256
+ # REINDEX INDEX CONCURRENTLY leaves a new invalid index if it fails, so use remove_index instead
257
+ def remove_invalid_index_if_needed(*args)
258
+ options = args.extract_options!
259
+
260
+ # ensures has same options as existing index
261
+ # check args to avoid errors with index_exists?
262
+ return unless args.size == 2 && connection.index_exists?(*args, **options.merge(valid: false))
263
+
264
+ table, columns = args
265
+ index_name = options.fetch(:name, connection.index_name(table, columns))
266
+
267
+ # valid option is ignored for Active Record < 7.1, so check name as well
268
+ return if ar_version < 7.1 && !adapter.index_invalid?(table, index_name)
269
+
270
+ @migration.say("Attempting to remove invalid index")
271
+ without_retries do
272
+ # TODO pass index schema for extra safety?
273
+ @migration.remove_index(table, **{name: index_name}.merge(options.slice(:algorithm)))
274
+ end
275
+ end
210
276
  end
211
277
  end
@@ -10,7 +10,7 @@ module StrongMigrations
10
10
  if !new_table?(table)
11
11
  if postgresql? && options[:validate] != false
12
12
  add_options = options.merge(validate: false)
13
- name = options[:name] || @migration.check_constraint_options(table, expression, options)[:name]
13
+ name = options[:name] || connection.check_constraint_options(table, expression, options)[:name]
14
14
  validate_options = {name: name}
15
15
 
16
16
  if StrongMigrations.safe_by_default
@@ -228,32 +228,30 @@ module StrongMigrations
228
228
  table, column, null, default = args
229
229
  if !null
230
230
  if postgresql?
231
- safe = adapter.constraints(table).any? { |c| c["def"] == "CHECK ((#{column} IS NOT NULL))" || c["def"] == "CHECK ((#{connection.quote_column_name(column)} IS NOT NULL))" }
231
+ constraints = connection.check_constraints(table)
232
+ safe = constraints.any? { |c| c.options[:validate] && (c.expression == "#{column} IS NOT NULL" || c.expression == "#{connection.quote_column_name(column)} IS NOT NULL") }
232
233
 
233
234
  unless safe
234
235
  # match https://github.com/nullobject/rein
235
236
  constraint_name = "#{table}_#{column}_null"
236
237
 
237
- add_code = constraint_str("ALTER TABLE %s ADD CONSTRAINT %s CHECK (%s IS NOT NULL) NOT VALID", [table, constraint_name, column])
238
- validate_code = constraint_str("ALTER TABLE %s VALIDATE CONSTRAINT %s", [table, constraint_name])
239
- remove_code = constraint_str("ALTER TABLE %s DROP CONSTRAINT %s", [table, constraint_name])
240
-
241
- validate_constraint_code = String.new(command_str(:validate_check_constraint, [table, {name: constraint_name}]))
242
-
238
+ add_args = [table, "#{quote_column_if_needed(column)} IS NOT NULL", {name: constraint_name, validate: false}]
239
+ validate_args = [table, {name: constraint_name}]
243
240
  change_args = [table, column, null]
244
-
245
- validate_constraint_code << "\n #{command_str(:change_column_null, change_args)}"
246
- validate_constraint_code << "\n #{command_str(:remove_check_constraint, [table, {name: constraint_name}])}"
241
+ remove_args = [table, {name: constraint_name}]
247
242
 
248
243
  if StrongMigrations.safe_by_default
249
- safe_change_column_null(add_code, validate_code, change_args, remove_code, default)
244
+ safe_change_column_null(add_args, validate_args, change_args, remove_args, default, constraints)
250
245
  throw :safe
251
246
  end
252
247
 
253
- add_constraint_code = command_str(:add_check_constraint, [table, "#{quote_column_if_needed(column)} IS NOT NULL", {name: constraint_name, validate: false}])
248
+ add_constraint_code = command_str(:add_check_constraint, add_args)
254
249
 
250
+ up_code = String.new(command_str(:validate_check_constraint, validate_args))
251
+ up_code << "\n #{command_str(:change_column_null, change_args)}"
252
+ up_code << "\n #{command_str(:remove_check_constraint, remove_args)}"
255
253
  down_code = "#{add_constraint_code}\n #{command_str(:change_column_null, [table, column, true])}"
256
- validate_constraint_code = "def up\n #{validate_constraint_code}\n end\n\n def down\n #{down_code}\n end"
254
+ validate_constraint_code = "def up\n #{up_code}\n end\n\n def down\n #{down_code}\n end"
257
255
 
258
256
  raise_error :change_column_null_postgresql,
259
257
  add_constraint_code: add_constraint_code,
@@ -302,29 +300,28 @@ module StrongMigrations
302
300
  columns =
303
301
  case method
304
302
  when :remove_timestamps
305
- ["created_at", "updated_at"]
303
+ [:created_at, :updated_at]
306
304
  when :remove_column
307
- [args[1].to_s]
305
+ [args[1]]
308
306
  when :remove_columns
309
- # Active Record 6.1+ supports options
310
307
  if args.last.is_a?(Hash)
311
- args[1..-2].map(&:to_s)
308
+ args[1..-2]
312
309
  else
313
- args[1..-1].map(&:to_s)
310
+ args[1..-1]
314
311
  end
315
312
  else
316
313
  options = args[2] || {}
317
314
  reference = args[1]
318
315
  cols = []
319
- cols << "#{reference}_type" if options[:polymorphic]
320
- cols << "#{reference}_id"
316
+ cols << "#{reference}_type".to_sym if options[:polymorphic]
317
+ cols << "#{reference}_id".to_sym
321
318
  cols
322
319
  end
323
320
 
324
- code = "self.ignored_columns += #{columns.inspect}"
321
+ code = "self.ignored_columns += #{columns.map(&:to_s).inspect}"
325
322
 
326
323
  raise_error :remove_column,
327
- model: args[0].to_s.classify,
324
+ model: model_name(args[0]),
328
325
  code: code,
329
326
  command: command_str(method, args),
330
327
  column_suffix: columns.size > 1 ? "s" : ""
@@ -392,7 +389,7 @@ module StrongMigrations
392
389
  message = message + append if append
393
390
 
394
391
  vars[:migration_name] = @migration.class.name
395
- vars[:migration_suffix] = "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
392
+ vars[:migration_suffix] = migration_suffix
396
393
  vars[:base_model] = "ApplicationRecord"
397
394
 
398
395
  # escape % not followed by {
@@ -433,7 +430,7 @@ module StrongMigrations
433
430
  end
434
431
 
435
432
  def backfill_code(table, column, default, function = false)
436
- model = table.to_s.classify
433
+ model = model_name(table)
437
434
  if function
438
435
  # update_all(column: Arel.sql(default)) also works in newer versions of Active Record
439
436
  update_expr = "#{quote_column_if_needed(column)} = #{default}"
@@ -456,5 +453,13 @@ module StrongMigrations
456
453
  def new_column?(table, column)
457
454
  new_table?(table) || @new_columns.include?([table.to_s, column.to_s])
458
455
  end
456
+
457
+ def migration_suffix
458
+ "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
459
+ end
460
+
461
+ def model_name(table)
462
+ table.to_s.classify
463
+ end
459
464
  end
460
465
  end
@@ -147,7 +147,7 @@ you're doing is safe before proceeding, then wrap it in a safety_assured { ... }
147
147
  create_table:
148
148
  "The force option will destroy existing tables.
149
149
  If this is intended, drop the existing table first.
150
- Otherwise, remove the force option.",
150
+ In any case, remove the force option.",
151
151
 
152
152
  execute:
153
153
  "Strong Migrations does not support inspecting what happens inside an
@@ -18,7 +18,8 @@ module StrongMigrations
18
18
  end
19
19
  end
20
20
  end
21
- ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
21
+ # same as ActiveRecord::Migration
22
+ ruby2_keywords(:method_missing)
22
23
 
23
24
  def revert(*)
24
25
  if strong_migrations_checker.version_safe?
@@ -1,8 +1,26 @@
1
1
  module StrongMigrations
2
- module DatabaseTasks
3
- def migrate(...)
2
+ module MigrationContext
3
+ def up(...)
4
4
  super
5
5
  rescue => e
6
+ strong_migrations_process_exception(e)
7
+ end
8
+
9
+ def down(...)
10
+ super
11
+ rescue => e
12
+ strong_migrations_process_exception(e)
13
+ end
14
+
15
+ def run(...)
16
+ super
17
+ rescue => e
18
+ strong_migrations_process_exception(e)
19
+ end
20
+
21
+ private
22
+
23
+ def strong_migrations_process_exception(e)
6
24
  if e.cause.is_a?(StrongMigrations::Error)
7
25
  # strip cause and clean backtrace
8
26
  def e.cause
@@ -6,11 +6,13 @@ module StrongMigrations
6
6
  # handle MigrationProxy class
7
7
  migration = migration.send(:migration) if migration.respond_to?(:migration, true)
8
8
 
9
- # retry migration since the entire transaction needs to be rerun
10
9
  checker = migration.send(:strong_migrations_checker)
10
+ return super if checker.skip?
11
+
12
+ # retry migration since the entire transaction needs to be rerun
11
13
  checker.retry_lock_timeouts(check_committed: true) do
12
14
  # failed transaction reverts timeout, so need to re-apply
13
- checker.timeouts_set = false
15
+ checker.reset
14
16
 
15
17
  super(migration, ...)
16
18
  end
@@ -4,7 +4,6 @@ module StrongMigrations
4
4
  StrongMigrations.safe_by_default && [:add_index, :add_belongs_to, :add_reference, :remove_index, :add_foreign_key, :add_check_constraint, :change_column_null].include?(method)
5
5
  end
6
6
 
7
- # TODO check if invalid index with expected name exists and remove if needed
8
7
  def safe_add_index(*args, **options)
9
8
  disable_transaction
10
9
  @migration.add_index(*args, **options.merge(algorithm: :concurrently))
@@ -47,14 +46,16 @@ module StrongMigrations
47
46
  def safe_add_foreign_key(from_table, to_table, *args, **options)
48
47
  @migration.reversible do |dir|
49
48
  dir.up do
50
- @migration.add_foreign_key(from_table, to_table, *args, **options.merge(validate: false))
49
+ # validate option is unintentionally ignored for Active Record < 7.1
50
+ # https://github.com/rails/rails/pull/45896
51
+ if !connection.foreign_key_exists?(from_table, to_table, **options.merge(validate: false))
52
+ @migration.add_foreign_key(from_table, to_table, *args, **options.merge(validate: false))
53
+ end
51
54
  disable_transaction
52
- validate_options = options.slice(:column, :name)
53
- @migration.validate_foreign_key(from_table, to_table, **validate_options)
55
+ @migration.validate_foreign_key(from_table, to_table, **options.slice(:column, :name))
54
56
  end
55
57
  dir.down do
56
- remove_options = options.slice(:column, :name)
57
- @migration.remove_foreign_key(from_table, to_table, **remove_options)
58
+ @migration.remove_foreign_key(from_table, to_table, **options.slice(:column, :name))
58
59
  end
59
60
  end
60
61
  end
@@ -62,7 +63,10 @@ module StrongMigrations
62
63
  def safe_add_check_constraint(table, expression, *args, add_options, validate_options)
63
64
  @migration.reversible do |dir|
64
65
  dir.up do
65
- @migration.add_check_constraint(table, expression, *args, **add_options)
66
+ # only skip invalid constraints
67
+ unless connection.check_constraints(table).any? { |c| c.options[:name] == validate_options[:name] && !c.options[:validate] }
68
+ @migration.add_check_constraint(table, expression, *args, **add_options)
69
+ end
66
70
  disable_transaction
67
71
  @migration.validate_check_constraint(table, **validate_options)
68
72
  end
@@ -72,35 +76,33 @@ module StrongMigrations
72
76
  end
73
77
  end
74
78
 
75
- def safe_change_column_null(add_code, validate_code, change_args, remove_code, default)
79
+ def safe_change_column_null(add_args, validate_args, change_args, remove_args, default, constraints)
76
80
  @migration.reversible do |dir|
77
81
  dir.up do
78
82
  unless default.nil?
79
83
  raise Error, "default value not supported yet with safe_by_default"
80
84
  end
81
85
 
82
- @migration.safety_assured do
83
- @migration.execute(add_code)
84
- disable_transaction
85
- @migration.execute(validate_code)
86
- end
87
- if change_args
88
- @migration.change_column_null(*change_args)
89
- @migration.safety_assured do
90
- @migration.execute(remove_code)
91
- end
86
+ add_options = add_args.extract_options!
87
+ validate_options = validate_args.extract_options!
88
+ remove_options = remove_args.extract_options!
89
+
90
+ # only skip invalid constraints
91
+ unless constraints.any? { |c| c.options[:name] == validate_options[:name] && !c.options[:validate] }
92
+ @migration.add_check_constraint(*add_args, **add_options)
92
93
  end
94
+ disable_transaction
95
+
96
+ connection.begin_db_transaction
97
+ @migration.validate_check_constraint(*validate_args, **validate_options)
98
+ @migration.change_column_null(*change_args)
99
+ @migration.remove_check_constraint(*remove_args, **remove_options)
100
+ connection.commit_db_transaction
93
101
  end
94
102
  dir.down do
95
- if change_args
96
- down_args = change_args.dup
97
- down_args[2] = true
98
- @migration.change_column_null(*down_args)
99
- else
100
- @migration.safety_assured do
101
- @migration.execute(remove_code)
102
- end
103
- end
103
+ down_args = change_args.dup
104
+ down_args[2] = true
105
+ @migration.change_column_null(*down_args)
104
106
  end
105
107
  end
106
108
  end
@@ -109,13 +111,13 @@ module StrongMigrations
109
111
  # so just commit at start
110
112
  def disable_transaction
111
113
  if in_transaction? && !transaction_disabled
112
- @migration.connection.commit_db_transaction
114
+ connection.commit_db_transaction
113
115
  self.transaction_disabled = true
114
116
  end
115
117
  end
116
118
 
117
119
  def in_transaction?
118
- @migration.connection.open_transactions > 0
120
+ connection.open_transactions > 0
119
121
  end
120
122
  end
121
123
  end
@@ -1,3 +1,3 @@
1
1
  module StrongMigrations
2
- VERSION = "2.0.2"
2
+ VERSION = "2.1.0"
3
3
  end
@@ -11,8 +11,8 @@ require_relative "strong_migrations/adapters/postgresql_adapter"
11
11
  require_relative "strong_migrations/checks"
12
12
  require_relative "strong_migrations/safe_methods"
13
13
  require_relative "strong_migrations/checker"
14
- require_relative "strong_migrations/database_tasks"
15
14
  require_relative "strong_migrations/migration"
15
+ require_relative "strong_migrations/migration_context"
16
16
  require_relative "strong_migrations/migrator"
17
17
  require_relative "strong_migrations/version"
18
18
 
@@ -29,7 +29,7 @@ module StrongMigrations
29
29
  :target_postgresql_version, :target_mysql_version, :target_mariadb_version,
30
30
  :enabled_checks, :lock_timeout, :statement_timeout, :check_down, :target_version,
31
31
  :safe_by_default, :target_sql_mode, :lock_timeout_retries, :lock_timeout_retry_delay,
32
- :alphabetize_schema
32
+ :alphabetize_schema, :skipped_databases, :remove_invalid_indexes
33
33
  attr_writer :lock_timeout_limit
34
34
  end
35
35
  self.auto_analyze = false
@@ -40,6 +40,8 @@ module StrongMigrations
40
40
  self.safe_by_default = false
41
41
  self.check_down = false
42
42
  self.alphabetize_schema = false
43
+ self.skipped_databases = []
44
+ self.remove_invalid_indexes = false
43
45
 
44
46
  # private
45
47
  def self.developer_env?
@@ -83,6 +85,10 @@ module StrongMigrations
83
85
  false
84
86
  end
85
87
  end
88
+
89
+ def self.skip_database(database)
90
+ self.skipped_databases << database
91
+ end
86
92
  end
87
93
 
88
94
  # load error messages
@@ -90,12 +96,9 @@ require_relative "strong_migrations/error_messages"
90
96
 
91
97
  ActiveSupport.on_load(:active_record) do
92
98
  ActiveRecord::Migration.prepend(StrongMigrations::Migration)
99
+ ActiveRecord::MigrationContext.prepend(StrongMigrations::MigrationContext)
93
100
  ActiveRecord::Migrator.prepend(StrongMigrations::Migrator)
94
101
 
95
- if defined?(ActiveRecord::Tasks::DatabaseTasks)
96
- ActiveRecord::Tasks::DatabaseTasks.singleton_class.prepend(StrongMigrations::DatabaseTasks)
97
- end
98
-
99
102
  require_relative "strong_migrations/schema_dumper"
100
103
  ActiveRecord::SchemaDumper.prepend(StrongMigrations::SchemaDumper)
101
104
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: strong_migrations
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2024-10-30 00:00:00.000000000 Z
13
+ date: 2024-11-09 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord
@@ -47,9 +47,9 @@ files:
47
47
  - lib/strong_migrations/adapters/postgresql_adapter.rb
48
48
  - lib/strong_migrations/checker.rb
49
49
  - lib/strong_migrations/checks.rb
50
- - lib/strong_migrations/database_tasks.rb
51
50
  - lib/strong_migrations/error_messages.rb
52
51
  - lib/strong_migrations/migration.rb
52
+ - lib/strong_migrations/migration_context.rb
53
53
  - lib/strong_migrations/migrator.rb
54
54
  - lib/strong_migrations/railtie.rb
55
55
  - lib/strong_migrations/safe_methods.rb
@@ -75,7 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
75
75
  - !ruby/object:Gem::Version
76
76
  version: '0'
77
77
  requirements: []
78
- rubygems_version: 3.5.16
78
+ rubygems_version: 3.5.22
79
79
  signing_key:
80
80
  specification_version: 4
81
81
  summary: Catch unsafe migrations in development