strong_migrations 2.2.1 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a78b56711a718210a3d14c82a4c30734c0b653827c1a2bdcc7617c27a7eed0e5
4
- data.tar.gz: 9ef73e9cd8783a34d330c3517d98df50266427bd10bd92656cc528753aceb045
3
+ metadata.gz: d290d3cb9db86b312fcdeb417aa4b24838f9135df9526dff1e525e12484ae160
4
+ data.tar.gz: e169be188a233de23f961b4aefe3997e71666a37fcc581d3cc95973b20b1642e
5
5
  SHA512:
6
- metadata.gz: 0ece935c993dad2d316d183be938db27a9595cac2d0880f5fcd3f516965440bda73da9e8ad5239269451b991b04a897de03b8f0ed11b30400074daf30fa353b7
7
- data.tar.gz: c8a18a098a6356daae4bf6bb827fa7f11c0a37e8b88712083af88f4eeb0f8dcda7f893491f1a044cc2399afc8147c31c3b07bf46a2155b972c78bb2aebbd479e
6
+ metadata.gz: 61adf361956734e8944d8dea110ee77b22db8e24cde13085a5b90f35b09f684632b33ab040632f4dc0656853f12d1255204e6736881656a50a462e041f1e56f1
7
+ data.tar.gz: 7f0d1650bbb8e16e062c24defe37fec194d0f98ccd458d21db39d7664ffa51c8340a017d6ce8c1ace8018ba86da7795df881dfb19d31d578a41870ad82d55a52
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## 2.4.0 (2025-06-23)
2
+
3
+ - Added `remove_invalid_indexes` option to install generator
4
+ - Added experimental `transaction_timeout` option
5
+ - Dropped support for Ruby < 3.2 and Active Record < 7.1
6
+
7
+ ## 2.3.0 (2025-04-03)
8
+
9
+ - Added check for `change_column` for columns with check constraints with Postgres
10
+
1
11
  ## 2.2.1 (2025-03-21)
2
12
 
3
13
  - Added support for `change_column_null` with default value with `safe_by_default` option
data/README.md CHANGED
@@ -882,8 +882,6 @@ To automatically remove the invalid index when the migration runs again, use:
882
882
  StrongMigrations.remove_invalid_indexes = true
883
883
  ```
884
884
 
885
- Note: This feature is experimental.
886
-
887
885
  ## Lock Timeout Retries
888
886
 
889
887
  Note: This feature is experimental.
@@ -20,6 +20,9 @@ StrongMigrations.auto_analyze = true
20
20
  # end
21
21
  # end<% if postgresql? %>
22
22
 
23
+ # Remove invalid indexes when rerunning migrations
24
+ # StrongMigrations.remove_invalid_indexes = true
25
+
23
26
  # Make some operations safe by default
24
27
  # See https://github.com/ankane/strong_migrations#safe-by-default
25
28
  # StrongMigrations.safe_by_default = true<% end %>
@@ -16,6 +16,10 @@ module StrongMigrations
16
16
  # do nothing
17
17
  end
18
18
 
19
+ def set_transaction_timeout(timeout)
20
+ # do nothing
21
+ end
22
+
19
23
  def set_lock_timeout(timeout)
20
24
  # do nothing
21
25
  end
@@ -23,6 +23,11 @@ module StrongMigrations
23
23
  set_timeout("statement_timeout", timeout)
24
24
  end
25
25
 
26
+ def set_transaction_timeout(timeout)
27
+ # TODO make sure true version supports it as well?
28
+ set_timeout("transaction_timeout", timeout) if server_version >= Gem::Version.new("17")
29
+ end
30
+
26
31
  def set_lock_timeout(timeout)
27
32
  set_timeout("lock_timeout", timeout)
28
33
  end
@@ -127,21 +132,6 @@ module StrongMigrations
127
132
  safe
128
133
  end
129
134
 
130
- # TODO remove when Active Record < 7.1 is no longer supported
131
- def index_invalid?(table_name, index_name)
132
- query = <<~SQL
133
- SELECT
134
- indisvalid
135
- FROM
136
- pg_index
137
- WHERE
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
- SQL
142
- select_all(query.squish).any?
143
- end
144
-
145
135
  def writes_blocked?
146
136
  query = <<~SQL
147
137
  SELECT
@@ -177,6 +167,11 @@ module StrongMigrations
177
167
  63
178
168
  end
179
169
 
170
+ def constraints(table, column)
171
+ # TODO improve column check
172
+ connection.check_constraints(table).select { |c| /\b#{Regexp.escape(column.to_s)}\b/.match?(c.expression) }
173
+ end
174
+
180
175
  private
181
176
 
182
177
  def set_timeout(setting, timeout)
@@ -181,6 +181,9 @@ module StrongMigrations
181
181
  if StrongMigrations.statement_timeout
182
182
  adapter.set_statement_timeout(StrongMigrations.statement_timeout)
183
183
  end
184
+ if StrongMigrations.transaction_timeout
185
+ adapter.set_transaction_timeout(StrongMigrations.transaction_timeout)
186
+ end
184
187
  if StrongMigrations.lock_timeout
185
188
  adapter.set_lock_timeout(StrongMigrations.lock_timeout)
186
189
  end
@@ -264,9 +267,6 @@ module StrongMigrations
264
267
  table, columns = args
265
268
  index_name = options.fetch(:name, connection.index_name(table, columns))
266
269
 
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
270
  @migration.say("Attempting to remove invalid index")
271
271
  without_retries do
272
272
  # TODO pass index schema for extra safety?
@@ -210,6 +210,32 @@ module StrongMigrations
210
210
  end
211
211
 
212
212
  raise_error :change_column, rewrite_blocks: adapter.rewrite_blocks unless safe
213
+
214
+ # constraints must be rechecked
215
+ # Postgres recommends dropping constraints before and adding them back
216
+ # https://www.postgresql.org/docs/current/ddl-alter.html#DDL-ALTER-COLUMN-TYPE
217
+ if postgresql?
218
+ constraints = adapter.constraints(table, column)
219
+ if constraints.any?
220
+ change_commands = []
221
+ constraints.each do |c|
222
+ change_commands << command_str(:remove_check_constraint, [table, c.expression, {name: c.name}])
223
+ end
224
+ change_commands << command_str(:change_column, args + [options])
225
+ constraints.each do |c|
226
+ change_commands << command_str(:add_check_constraint, [table, c.expression, {name: c.name, validate: false}])
227
+ end
228
+
229
+ validate_commands = []
230
+ constraints.each do |c|
231
+ validate_commands << command_str(:validate_check_constraint, [table, {name: c.name}])
232
+ end
233
+
234
+ raise_error :change_column_constraint,
235
+ change_column_code: change_commands.join("\n "),
236
+ validate_constraint_code: validate_commands.join("\n ")
237
+ end
238
+ end
213
239
  end
214
240
 
215
241
  def check_change_column_default(*args)
@@ -60,6 +60,22 @@ while the entire table is rewritten. A safer approach is to:
60
60
  change_column_with_not_null:
61
61
  "Changing the type is safe, but setting NOT NULL is not.",
62
62
 
63
+ change_column_constraint: "Changing the type of a column that has check constraints blocks reads and writes
64
+ while every row is checked. Drop the check constraints on the column before
65
+ changing the type and add them back afterwards.
66
+
67
+ class %{migration_name} < ActiveRecord::Migration%{migration_suffix}
68
+ def change
69
+ %{change_column_code}
70
+ end
71
+ end
72
+
73
+ class Validate%{migration_name} < ActiveRecord::Migration%{migration_suffix}
74
+ def change
75
+ %{validate_constraint_code}
76
+ end
77
+ end",
78
+
63
79
  remove_column: "Active Record caches attributes, which causes problems
64
80
  when removing columns. Be sure to ignore the column%{column_suffix}:
65
81
 
@@ -7,10 +7,7 @@ module StrongMigrations
7
7
  end
8
8
 
9
9
  def method_missing(method, *args)
10
- return super if is_a?(ActiveRecord::Schema)
11
-
12
- # Active Record 7.0.2+ versioned schema
13
- return super if defined?(ActiveRecord::Schema::Definition) && is_a?(ActiveRecord::Schema::Definition)
10
+ return super if is_a?(ActiveRecord::Schema) || is_a?(ActiveRecord::Schema::Definition)
14
11
 
15
12
  catch(:safe) do
16
13
  strong_migrations_checker.perform(method, *args) do
@@ -4,7 +4,7 @@ module StrongMigrations
4
4
  return super unless StrongMigrations.lock_timeout_retries > 0 && use_transaction?(migration)
5
5
 
6
6
  # handle MigrationProxy class
7
- migration = migration.send(:migration) if migration.respond_to?(:migration, true)
7
+ migration = migration.send(:migration) if !migration.is_a?(ActiveRecord::Migration) && migration.respond_to?(:migration, true)
8
8
 
9
9
  checker = migration.send(:strong_migrations_checker)
10
10
  return super if checker.skip?
@@ -46,8 +46,6 @@ module StrongMigrations
46
46
  def safe_add_foreign_key(from_table, to_table, *args, **options)
47
47
  @migration.reversible do |dir|
48
48
  dir.up do
49
- # validate option is unintentionally ignored for Active Record < 7.1
50
- # https://github.com/rails/rails/pull/45896
51
49
  if !connection.foreign_key_exists?(from_table, to_table, **options.merge(validate: false))
52
50
  @migration.add_foreign_key(from_table, to_table, *args, **options.merge(validate: false))
53
51
  end
@@ -1,3 +1,3 @@
1
1
  module StrongMigrations
2
- VERSION = "2.2.1"
2
+ VERSION = "2.4.0"
3
3
  end
@@ -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, :skipped_databases, :remove_invalid_indexes
32
+ :alphabetize_schema, :skipped_databases, :remove_invalid_indexes, :transaction_timeout
33
33
  attr_writer :lock_timeout_limit
34
34
  end
35
35
  self.auto_analyze = false
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.2.1
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
@@ -9,7 +9,7 @@ authors:
9
9
  - David Waller
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2025-03-22 00:00:00.000000000 Z
12
+ date: 1980-01-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -17,14 +17,14 @@ dependencies:
17
17
  requirements:
18
18
  - - ">="
19
19
  - !ruby/object:Gem::Version
20
- version: '7'
20
+ version: '7.1'
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - ">="
26
26
  - !ruby/object:Gem::Version
27
- version: '7'
27
+ version: '7.1'
28
28
  email:
29
29
  - andrew@ankane.org
30
30
  - bob.remeika@gmail.com
@@ -65,14 +65,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '3.1'
68
+ version: '3.2'
69
69
  required_rubygems_version: !ruby/object:Gem::Requirement
70
70
  requirements:
71
71
  - - ">="
72
72
  - !ruby/object:Gem::Version
73
73
  version: '0'
74
74
  requirements: []
75
- rubygems_version: 3.6.2
75
+ rubygems_version: 3.6.7
76
76
  specification_version: 4
77
77
  summary: Catch unsafe migrations in development
78
78
  test_files: []