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.
@@ -5,22 +5,22 @@ module StrongMigrations
5
5
  end
6
6
 
7
7
  # TODO check if invalid index with expected name exists and remove if needed
8
- def safe_add_index(table, columns, options)
8
+ def safe_add_index(*args, **options)
9
9
  disable_transaction
10
- @migration.add_index(table, columns, **options.merge(algorithm: :concurrently))
10
+ @migration.add_index(*args, **options.merge(algorithm: :concurrently))
11
11
  end
12
12
 
13
- def safe_remove_index(table, options)
13
+ def safe_remove_index(*args, **options)
14
14
  disable_transaction
15
- @migration.remove_index(table, **options.merge(algorithm: :concurrently))
15
+ @migration.remove_index(*args, **options.merge(algorithm: :concurrently))
16
16
  end
17
17
 
18
- def safe_add_reference(table, reference, options)
18
+ def safe_add_reference(table, reference, *args, **options)
19
19
  @migration.reversible do |dir|
20
20
  dir.up do
21
21
  disable_transaction
22
22
  foreign_key = options.delete(:foreign_key)
23
- @migration.add_reference(table, reference, **options)
23
+ @migration.add_reference(table, reference, *args, **options)
24
24
  if foreign_key
25
25
  # same as Active Record
26
26
  name =
@@ -43,10 +43,10 @@ module StrongMigrations
43
43
  end
44
44
  end
45
45
 
46
- def safe_add_foreign_key(from_table, to_table, options)
46
+ def safe_add_foreign_key(from_table, to_table, *args, **options)
47
47
  @migration.reversible do |dir|
48
48
  dir.up do
49
- @migration.add_foreign_key(from_table, to_table, **options.merge(validate: false))
49
+ @migration.add_foreign_key(from_table, to_table, *args, **options.merge(validate: false))
50
50
  disable_transaction
51
51
  @migration.validate_foreign_key(from_table, to_table)
52
52
  end
@@ -56,10 +56,10 @@ module StrongMigrations
56
56
  end
57
57
  end
58
58
 
59
- def safe_add_check_constraint(table, expression, add_options, validate_options)
59
+ def safe_add_check_constraint(table, expression, *args, add_options, validate_options)
60
60
  @migration.reversible do |dir|
61
61
  dir.up do
62
- @migration.add_check_constraint(table, expression, **add_options)
62
+ @migration.add_check_constraint(table, expression, *args, **add_options)
63
63
  disable_transaction
64
64
  @migration.validate_check_constraint(table, **validate_options)
65
65
  end
@@ -69,9 +69,13 @@ module StrongMigrations
69
69
  end
70
70
  end
71
71
 
72
- def safe_change_column_null(add_code, validate_code, change_args, remove_code)
72
+ def safe_change_column_null(add_code, validate_code, change_args, remove_code, default)
73
73
  @migration.reversible do |dir|
74
74
  dir.up do
75
+ unless default.nil?
76
+ raise Error, "default value not supported yet with safe_by_default"
77
+ end
78
+
75
79
  @migration.safety_assured do
76
80
  @migration.execute(add_code)
77
81
  disable_transaction
@@ -1,3 +1,3 @@
1
1
  module StrongMigrations
2
- VERSION = "0.8.0"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -1,11 +1,19 @@
1
1
  # dependencies
2
2
  require "active_support"
3
3
 
4
+ # adapters
5
+ require "strong_migrations/adapters/abstract_adapter"
6
+ require "strong_migrations/adapters/mysql_adapter"
7
+ require "strong_migrations/adapters/mariadb_adapter"
8
+ require "strong_migrations/adapters/postgresql_adapter"
9
+
4
10
  # modules
11
+ require "strong_migrations/checks"
5
12
  require "strong_migrations/safe_methods"
6
13
  require "strong_migrations/checker"
7
14
  require "strong_migrations/database_tasks"
8
15
  require "strong_migrations/migration"
16
+ require "strong_migrations/migrator"
9
17
  require "strong_migrations/version"
10
18
 
11
19
  # integrations
@@ -14,235 +22,21 @@ require "strong_migrations/railtie" if defined?(Rails)
14
22
  module StrongMigrations
15
23
  class Error < StandardError; end
16
24
  class UnsafeMigration < Error; end
25
+ class UnsupportedVersion < Error; end
17
26
 
18
27
  class << self
19
28
  attr_accessor :auto_analyze, :start_after, :checks, :error_messages,
20
29
  :target_postgresql_version, :target_mysql_version, :target_mariadb_version,
21
30
  :enabled_checks, :lock_timeout, :statement_timeout, :check_down, :target_version,
22
- :safe_by_default
31
+ :safe_by_default, :target_sql_mode, :lock_timeout_retries, :lock_timeout_retry_delay
23
32
  attr_writer :lock_timeout_limit
24
33
  end
25
34
  self.auto_analyze = false
26
35
  self.start_after = 0
36
+ self.lock_timeout_retries = 0
37
+ self.lock_timeout_retry_delay = 10 # seconds
27
38
  self.checks = []
28
39
  self.safe_by_default = false
29
- self.error_messages = {
30
- add_column_default:
31
- "Adding a column with a non-null default blocks %{rewrite_blocks} while the entire table is rewritten.
32
- Instead, add the column without a default value, then change the default.
33
-
34
- class %{migration_name} < ActiveRecord::Migration%{migration_suffix}
35
- def up
36
- %{add_command}
37
- %{change_command}
38
- end
39
-
40
- def down
41
- %{remove_command}
42
- end
43
- end
44
-
45
- Then backfill the existing rows in the Rails console or a separate migration with disable_ddl_transaction!.
46
-
47
- class Backfill%{migration_name} < ActiveRecord::Migration%{migration_suffix}
48
- disable_ddl_transaction!
49
-
50
- def up
51
- %{code}
52
- end
53
- end",
54
-
55
- add_column_json:
56
- "There's no equality operator for the json column type, which can cause errors for
57
- existing SELECT DISTINCT queries in your application. Use jsonb instead.
58
-
59
- class %{migration_name} < ActiveRecord::Migration%{migration_suffix}
60
- def change
61
- %{command}
62
- end
63
- end",
64
-
65
- change_column:
66
- "Changing the type of an existing column blocks %{rewrite_blocks}
67
- while the entire table is rewritten. A safer approach is to:
68
-
69
- 1. Create a new column
70
- 2. Write to both columns
71
- 3. Backfill data from the old column to the new column
72
- 4. Move reads from the old column to the new column
73
- 5. Stop writing to the old column
74
- 6. Drop the old column",
75
-
76
- change_column_with_not_null:
77
- "Changing the type is safe, but setting NOT NULL is not.",
78
-
79
- remove_column: "Active Record caches attributes, which causes problems
80
- when removing columns. Be sure to ignore the column%{column_suffix}:
81
-
82
- class %{model} < %{base_model}
83
- %{code}
84
- end
85
-
86
- Deploy the code, then wrap this step in a safety_assured { ... } block.
87
-
88
- class %{migration_name} < ActiveRecord::Migration%{migration_suffix}
89
- def change
90
- safety_assured { %{command} }
91
- end
92
- end",
93
-
94
- rename_column:
95
- "Renaming a column that's in use will cause errors
96
- in your application. A safer approach is to:
97
-
98
- 1. Create a new column
99
- 2. Write to both columns
100
- 3. Backfill data from the old column to new column
101
- 4. Move reads from the old column to the new column
102
- 5. Stop writing to the old column
103
- 6. Drop the old column",
104
-
105
- rename_table:
106
- "Renaming a table that's in use will cause errors
107
- in your application. A safer approach is to:
108
-
109
- 1. Create a new table. Don't forget to recreate indexes from the old table
110
- 2. Write to both tables
111
- 3. Backfill data from the old table to new table
112
- 4. Move reads from the old table to the new table
113
- 5. Stop writing to the old table
114
- 6. Drop the old table",
115
-
116
- add_reference:
117
- "%{headline} Instead, use:
118
-
119
- class %{migration_name} < ActiveRecord::Migration%{migration_suffix}
120
- disable_ddl_transaction!
121
-
122
- def change
123
- %{command}
124
- end
125
- end",
126
-
127
- add_index:
128
- "Adding an index non-concurrently blocks writes. Instead, use:
129
-
130
- class %{migration_name} < ActiveRecord::Migration%{migration_suffix}
131
- disable_ddl_transaction!
132
-
133
- def change
134
- %{command}
135
- end
136
- end",
137
-
138
- remove_index:
139
- "Removing an index non-concurrently blocks writes. Instead, use:
140
-
141
- class %{migration_name} < ActiveRecord::Migration%{migration_suffix}
142
- disable_ddl_transaction!
143
-
144
- def change
145
- %{command}
146
- end
147
- end",
148
-
149
- add_index_columns:
150
- "Adding a non-unique index with more than three columns rarely improves performance.
151
- Instead, start an index with columns that narrow down the results the most.",
152
-
153
- change_table:
154
- "Strong Migrations does not support inspecting what happens inside a
155
- change_table block, so cannot help you here. Please make really sure that what
156
- you're doing is safe before proceeding, then wrap it in a safety_assured { ... } block.",
157
-
158
- create_table:
159
- "The force option will destroy existing tables.
160
- If this is intended, drop the existing table first.
161
- Otherwise, remove the force option.",
162
-
163
- execute:
164
- "Strong Migrations does not support inspecting what happens inside an
165
- execute call, so cannot help you here. Please make really sure that what
166
- you're doing is safe before proceeding, then wrap it in a safety_assured { ... } block.",
167
-
168
- change_column_null:
169
- "Passing a default value to change_column_null runs a single UPDATE query,
170
- which can cause downtime. Instead, backfill the existing rows in the
171
- Rails console or a separate migration with disable_ddl_transaction!.
172
-
173
- class Backfill%{migration_name} < ActiveRecord::Migration%{migration_suffix}
174
- disable_ddl_transaction!
175
-
176
- def up
177
- %{code}
178
- end
179
- end",
180
-
181
- change_column_null_postgresql:
182
- "Setting NOT NULL on an existing column blocks reads and writes while every row is checked.
183
- Instead, add a check constraint and validate it in a separate migration.
184
-
185
- class %{migration_name} < ActiveRecord::Migration%{migration_suffix}
186
- def change
187
- %{add_constraint_code}
188
- end
189
- end
190
-
191
- class Validate%{migration_name} < ActiveRecord::Migration%{migration_suffix}
192
- def change
193
- %{validate_constraint_code}
194
- end
195
- end",
196
-
197
- change_column_null_mysql:
198
- "Setting NOT NULL on an existing column is not safe with your database engine.",
199
-
200
- add_foreign_key:
201
- "Adding a foreign key blocks writes on both tables. Instead,
202
- add the foreign key without validating existing rows,
203
- then validate them in a separate migration.
204
-
205
- class %{migration_name} < ActiveRecord::Migration%{migration_suffix}
206
- def change
207
- %{add_foreign_key_code}
208
- end
209
- end
210
-
211
- class Validate%{migration_name} < ActiveRecord::Migration%{migration_suffix}
212
- def change
213
- %{validate_foreign_key_code}
214
- end
215
- end",
216
-
217
- validate_foreign_key:
218
- "Validating a foreign key while writes are blocked is dangerous.
219
- Use disable_ddl_transaction! or a separate migration.",
220
-
221
- add_check_constraint:
222
- "Adding a check constraint key blocks reads and writes while every row is checked.
223
- Instead, add the check constraint without validating existing rows,
224
- then validate them in a separate migration.
225
-
226
- class %{migration_name} < ActiveRecord::Migration%{migration_suffix}
227
- def change
228
- %{add_check_constraint_code}
229
- end
230
- end
231
-
232
- class Validate%{migration_name} < ActiveRecord::Migration%{migration_suffix}
233
- def change
234
- %{validate_check_constraint_code}
235
- end
236
- end",
237
-
238
- add_check_constraint_mysql:
239
- "Adding a check constraint to an existing table is not safe with your database engine.",
240
-
241
- validate_check_constraint:
242
- "Validating a check constraint while writes are blocked is dangerous.
243
- Use disable_ddl_transaction! or a separate migration."
244
- }
245
- self.enabled_checks = (error_messages.keys - [:remove_index]).map { |k| [k, {}] }.to_h
246
40
  self.check_down = false
247
41
 
248
42
  # private
@@ -279,8 +73,12 @@ Use disable_ddl_transaction! or a separate migration."
279
73
  end
280
74
  end
281
75
 
76
+ # load error messages
77
+ require "strong_migrations/error_messages"
78
+
282
79
  ActiveSupport.on_load(:active_record) do
283
80
  ActiveRecord::Migration.prepend(StrongMigrations::Migration)
81
+ ActiveRecord::Migrator.prepend(StrongMigrations::Migrator)
284
82
 
285
83
  if defined?(ActiveRecord::Tasks::DatabaseTasks)
286
84
  ActiveRecord::Tasks::DatabaseTasks.singleton_class.prepend(StrongMigrations::DatabaseTasks)
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: 0.8.0
4
+ version: 1.2.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: 2022-02-10 00:00:00.000000000 Z
13
+ date: 2022-06-10 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord
@@ -41,10 +41,17 @@ files:
41
41
  - lib/generators/strong_migrations/install_generator.rb
42
42
  - lib/generators/strong_migrations/templates/initializer.rb.tt
43
43
  - lib/strong_migrations.rb
44
+ - lib/strong_migrations/adapters/abstract_adapter.rb
45
+ - lib/strong_migrations/adapters/mariadb_adapter.rb
46
+ - lib/strong_migrations/adapters/mysql_adapter.rb
47
+ - lib/strong_migrations/adapters/postgresql_adapter.rb
44
48
  - lib/strong_migrations/alphabetize_columns.rb
45
49
  - lib/strong_migrations/checker.rb
50
+ - lib/strong_migrations/checks.rb
46
51
  - lib/strong_migrations/database_tasks.rb
52
+ - lib/strong_migrations/error_messages.rb
47
53
  - lib/strong_migrations/migration.rb
54
+ - lib/strong_migrations/migrator.rb
48
55
  - lib/strong_migrations/railtie.rb
49
56
  - lib/strong_migrations/safe_methods.rb
50
57
  - lib/strong_migrations/version.rb
@@ -68,7 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
68
75
  - !ruby/object:Gem::Version
69
76
  version: '0'
70
77
  requirements: []
71
- rubygems_version: 3.3.3
78
+ rubygems_version: 3.3.7
72
79
  signing_key:
73
80
  specification_version: 4
74
81
  summary: Catch unsafe migrations in development