strong_migrations 1.6.0 → 2.8.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 +4 -4
- data/CHANGELOG.md +100 -1
- data/LICENSE.txt +1 -1
- data/README.md +421 -220
- data/lib/generators/strong_migrations/install_generator.rb +4 -8
- data/lib/generators/strong_migrations/templates/initializer.rb.tt +3 -0
- data/lib/strong_migrations/adapters/abstract_adapter.rb +13 -10
- data/lib/strong_migrations/adapters/mariadb_adapter.rb +2 -2
- data/lib/strong_migrations/adapters/mysql_adapter.rb +10 -5
- data/lib/strong_migrations/adapters/postgresql_adapter.rb +25 -28
- data/lib/strong_migrations/checker.rb +115 -19
- data/lib/strong_migrations/checks.rb +225 -97
- data/lib/strong_migrations/error_messages.rb +120 -14
- data/lib/strong_migrations/migration.rb +12 -6
- data/lib/strong_migrations/{database_tasks.rb → migration_context.rb} +20 -3
- data/lib/strong_migrations/migrator.rb +12 -6
- data/lib/strong_migrations/safe_methods.rb +60 -40
- data/lib/strong_migrations/schema_dumper.rb +15 -4
- data/lib/strong_migrations/version.rb +1 -1
- data/lib/strong_migrations.rb +9 -6
- metadata +7 -11
|
@@ -25,20 +25,25 @@ class Backfill%{migration_name} < ActiveRecord::Migration%{migration_suffix}
|
|
|
25
25
|
end
|
|
26
26
|
end",
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
|
|
28
|
+
add_column_default_callable:
|
|
29
|
+
"Strong Migrations does not support inspecting callable default values.
|
|
30
|
+
|
|
31
|
+
If the default value is %{default_type}, add the column without a default value, then change the default.
|
|
31
32
|
|
|
32
33
|
class %{migration_name} < ActiveRecord::Migration%{migration_suffix}
|
|
33
|
-
def
|
|
34
|
-
%{
|
|
34
|
+
def up
|
|
35
|
+
%{add_command}
|
|
36
|
+
%{change_command}, -> { ... }
|
|
35
37
|
end
|
|
36
|
-
end",
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
def down
|
|
40
|
+
%{remove_command}
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
Then backfill the existing rows in the Rails console or a separate migration with disable_ddl_transaction!.
|
|
45
|
+
|
|
46
|
+
Otherwise, wrap this step in a safety_assured { ... } block.",
|
|
42
47
|
|
|
43
48
|
add_column_json:
|
|
44
49
|
"There's no equality operator for the json column type, which can cause errors for
|
|
@@ -53,6 +58,9 @@ end",
|
|
|
53
58
|
add_column_generated_stored:
|
|
54
59
|
"Adding a stored generated column blocks %{rewrite_blocks} while the entire table is rewritten.",
|
|
55
60
|
|
|
61
|
+
add_column_auto_incrementing:
|
|
62
|
+
"Adding an auto-incrementing column blocks %{rewrite_blocks} while the entire table is rewritten.",
|
|
63
|
+
|
|
56
64
|
change_column:
|
|
57
65
|
"Changing the type of an existing column blocks %{rewrite_blocks}
|
|
58
66
|
while the entire table is rewritten. A safer approach is to:
|
|
@@ -67,6 +75,22 @@ while the entire table is rewritten. A safer approach is to:
|
|
|
67
75
|
change_column_with_not_null:
|
|
68
76
|
"Changing the type is safe, but setting NOT NULL is not.",
|
|
69
77
|
|
|
78
|
+
change_column_constraint: "Changing the type of a column that has check constraints blocks reads and writes
|
|
79
|
+
while every row is checked. Drop the check constraints on the column before
|
|
80
|
+
changing the type and add them back afterwards.
|
|
81
|
+
|
|
82
|
+
class %{migration_name} < ActiveRecord::Migration%{migration_suffix}
|
|
83
|
+
def change
|
|
84
|
+
%{change_column_code}
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
class Validate%{migration_name} < ActiveRecord::Migration%{migration_suffix}
|
|
89
|
+
def change
|
|
90
|
+
%{validate_constraint_code}
|
|
91
|
+
end
|
|
92
|
+
end",
|
|
93
|
+
|
|
70
94
|
remove_column: "Active Record caches attributes, which causes problems
|
|
71
95
|
when removing columns. Be sure to ignore the column%{column_suffix}:
|
|
72
96
|
|
|
@@ -88,18 +112,43 @@ in your application. A safer approach is to:
|
|
|
88
112
|
|
|
89
113
|
1. Create a new column
|
|
90
114
|
2. Write to both columns
|
|
91
|
-
3. Backfill data from the old column to new column
|
|
115
|
+
3. Backfill data from the old column to the new column
|
|
92
116
|
4. Move reads from the old column to the new column
|
|
93
117
|
5. Stop writing to the old column
|
|
94
118
|
6. Drop the old column",
|
|
95
119
|
|
|
120
|
+
rename_enum_value:
|
|
121
|
+
"Renaming an enum value that's in use will cause errors
|
|
122
|
+
in your application. A safer approach is to:
|
|
123
|
+
|
|
124
|
+
1. Add a new enum value before or after the old value
|
|
125
|
+
2. Update application code to handle both values and write the new value
|
|
126
|
+
3. Backfill data from the old value to the new value
|
|
127
|
+
|
|
128
|
+
class %{migration_name} < ActiveRecord::Migration%{migration_suffix}
|
|
129
|
+
def up
|
|
130
|
+
%{command}
|
|
131
|
+
end
|
|
132
|
+
end",
|
|
133
|
+
|
|
134
|
+
rename_schema:
|
|
135
|
+
"Renaming a schema that's in use will cause errors
|
|
136
|
+
in your application. A safer approach is to:
|
|
137
|
+
|
|
138
|
+
1. Create a new schema
|
|
139
|
+
2. Write to both schemas
|
|
140
|
+
3. Backfill data from the old schema to the new schema
|
|
141
|
+
4. Move reads from the old schema to the new schema
|
|
142
|
+
5. Stop writing to the old schema
|
|
143
|
+
6. Drop the old schema",
|
|
144
|
+
|
|
96
145
|
rename_table:
|
|
97
146
|
"Renaming a table that's in use will cause errors
|
|
98
147
|
in your application. A safer approach is to:
|
|
99
148
|
|
|
100
149
|
1. Create a new table. Don't forget to recreate indexes from the old table
|
|
101
150
|
2. Write to both tables
|
|
102
|
-
3. Backfill data from the old table to new table
|
|
151
|
+
3. Backfill data from the old table to the new table
|
|
103
152
|
4. Move reads from the old table to the new table
|
|
104
153
|
5. Stop writing to the old table
|
|
105
154
|
6. Drop the old table",
|
|
@@ -154,7 +203,7 @@ you're doing is safe before proceeding, then wrap it in a safety_assured { ... }
|
|
|
154
203
|
create_table:
|
|
155
204
|
"The force option will destroy existing tables.
|
|
156
205
|
If this is intended, drop the existing table first.
|
|
157
|
-
|
|
206
|
+
In any case, remove the force option.",
|
|
158
207
|
|
|
159
208
|
execute:
|
|
160
209
|
"Strong Migrations does not support inspecting what happens inside an
|
|
@@ -215,6 +264,28 @@ class Validate%{migration_name} < ActiveRecord::Migration%{migration_suffix}
|
|
|
215
264
|
end
|
|
216
265
|
end",
|
|
217
266
|
|
|
267
|
+
add_foreign_key_mysql:
|
|
268
|
+
"Adding a foreign key blocks writes on both tables. If you are 100% sure
|
|
269
|
+
all rows are valid and migrations do not use a connection pooler,
|
|
270
|
+
you can add the foreign key without validating existing rows.
|
|
271
|
+
|
|
272
|
+
class %{migration_name} < ActiveRecord::Migration%{migration_suffix}
|
|
273
|
+
def up
|
|
274
|
+
safety_assured do
|
|
275
|
+
begin
|
|
276
|
+
execute \"SET SESSION foreign_key_checks = 0\"
|
|
277
|
+
%{add_foreign_key_code}
|
|
278
|
+
ensure
|
|
279
|
+
execute \"SET SESSION foreign_key_checks = 1\"
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def down
|
|
285
|
+
%{remove_foreign_key_code}
|
|
286
|
+
end
|
|
287
|
+
end",
|
|
288
|
+
|
|
218
289
|
validate_foreign_key:
|
|
219
290
|
"Validating a foreign key while writes are blocked is dangerous.
|
|
220
291
|
Use disable_ddl_transaction! or a separate migration.",
|
|
@@ -244,7 +315,42 @@ end",
|
|
|
244
315
|
Use disable_ddl_transaction! or a separate migration.",
|
|
245
316
|
|
|
246
317
|
add_exclusion_constraint:
|
|
247
|
-
"Adding an exclusion constraint blocks reads and writes while every row is checked."
|
|
318
|
+
"Adding an exclusion constraint blocks reads and writes while every row is checked.",
|
|
319
|
+
|
|
320
|
+
add_unique_constraint:
|
|
321
|
+
"Adding a unique constraint creates a unique index, which blocks reads and writes.
|
|
322
|
+
Instead, create a unique index concurrently, then use it for the constraint.
|
|
323
|
+
|
|
324
|
+
class %{migration_name} < ActiveRecord::Migration%{migration_suffix}
|
|
325
|
+
disable_ddl_transaction!
|
|
326
|
+
|
|
327
|
+
def up
|
|
328
|
+
%{index_command}
|
|
329
|
+
%{constraint_command}
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def down
|
|
333
|
+
%{remove_command}
|
|
334
|
+
end
|
|
335
|
+
end",
|
|
336
|
+
|
|
337
|
+
copy_algorithm:
|
|
338
|
+
"Using the COPY algorithm blocks writes. Instead, use:
|
|
339
|
+
|
|
340
|
+
class %{migration_name} < ActiveRecord::Migration%{migration_suffix}
|
|
341
|
+
def change
|
|
342
|
+
%{command}
|
|
343
|
+
end
|
|
344
|
+
end",
|
|
345
|
+
|
|
346
|
+
lock_option:
|
|
347
|
+
"Using %{lock_type} locking blocks %{lock_blocks}. Instead, use:
|
|
348
|
+
|
|
349
|
+
class %{migration_name} < ActiveRecord::Migration%{migration_suffix}
|
|
350
|
+
def change
|
|
351
|
+
%{command}
|
|
352
|
+
end
|
|
353
|
+
end"
|
|
248
354
|
}
|
|
249
355
|
self.enabled_checks = (error_messages.keys - [:remove_index]).map { |k| [k, {}] }.to_h
|
|
250
356
|
end
|
|
@@ -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
|
|
@@ -18,10 +15,19 @@ module StrongMigrations
|
|
|
18
15
|
end
|
|
19
16
|
end
|
|
20
17
|
end
|
|
21
|
-
|
|
18
|
+
# same as ActiveRecord::Migration
|
|
19
|
+
ruby2_keywords(:method_missing)
|
|
20
|
+
|
|
21
|
+
def revert(*)
|
|
22
|
+
if strong_migrations_checker.version_safe?
|
|
23
|
+
safety_assured { super }
|
|
24
|
+
else
|
|
25
|
+
super
|
|
26
|
+
end
|
|
27
|
+
end
|
|
22
28
|
|
|
23
29
|
def safety_assured
|
|
24
|
-
strong_migrations_checker.safety_assured do
|
|
30
|
+
strong_migrations_checker.class.safety_assured do
|
|
25
31
|
yield
|
|
26
32
|
end
|
|
27
33
|
end
|
|
@@ -1,9 +1,26 @@
|
|
|
1
1
|
module StrongMigrations
|
|
2
|
-
module
|
|
3
|
-
|
|
4
|
-
def migrate(*args)
|
|
2
|
+
module MigrationContext
|
|
3
|
+
def up(...)
|
|
5
4
|
super
|
|
6
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)
|
|
7
24
|
if e.cause.is_a?(StrongMigrations::Error)
|
|
8
25
|
# strip cause and clean backtrace
|
|
9
26
|
def e.cause
|
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
module StrongMigrations
|
|
2
2
|
module Migrator
|
|
3
|
-
def ddl_transaction(migration,
|
|
4
|
-
|
|
3
|
+
def ddl_transaction(migration, ...)
|
|
4
|
+
retries = StrongMigrations.lock_timeout_retries > 0 && use_transaction?(migration)
|
|
5
|
+
return super unless retries || StrongMigrations.transaction_timeout
|
|
5
6
|
|
|
6
7
|
# handle MigrationProxy class
|
|
7
|
-
migration = migration.send(:migration) if migration.respond_to?(:migration, true)
|
|
8
|
+
migration = migration.send(:migration) if !migration.is_a?(ActiveRecord::Migration) && migration.respond_to?(:migration, true)
|
|
8
9
|
|
|
9
|
-
# retry migration since the entire transaction needs to be rerun
|
|
10
10
|
checker = migration.send(:strong_migrations_checker)
|
|
11
|
+
return super if checker.skip?
|
|
12
|
+
|
|
13
|
+
checker.set_transaction_timeout
|
|
14
|
+
return super unless retries
|
|
15
|
+
|
|
16
|
+
# retry migration since the entire transaction needs to be rerun
|
|
11
17
|
checker.retry_lock_timeouts(check_committed: true) do
|
|
12
18
|
# failed transaction reverts timeout, so need to re-apply
|
|
13
|
-
checker.
|
|
19
|
+
checker.reset
|
|
14
20
|
|
|
15
|
-
super(migration,
|
|
21
|
+
super(migration, ...)
|
|
16
22
|
end
|
|
17
23
|
end
|
|
18
24
|
end
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
module StrongMigrations
|
|
2
2
|
module SafeMethods
|
|
3
3
|
def safe_by_default_method?(method)
|
|
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)
|
|
4
|
+
StrongMigrations.safe_by_default && !version_safe? && [: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))
|
|
@@ -30,10 +29,11 @@ module StrongMigrations
|
|
|
30
29
|
(ActiveRecord::Base.pluralize_table_names ? reference.to_s.pluralize : reference).to_sym
|
|
31
30
|
end
|
|
32
31
|
|
|
32
|
+
foreign_key_opts = foreign_key.is_a?(Hash) ? foreign_key.except(:to_table) : {}
|
|
33
33
|
if reference
|
|
34
|
-
@migration.add_foreign_key(table, name, column: "#{reference}_id")
|
|
34
|
+
@migration.add_foreign_key(table, name, column: "#{reference}_id", **foreign_key_opts)
|
|
35
35
|
else
|
|
36
|
-
@migration.add_foreign_key(table, name)
|
|
36
|
+
@migration.add_foreign_key(table, name, **foreign_key_opts)
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
39
|
end
|
|
@@ -46,22 +46,14 @@ 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
|
-
|
|
50
|
-
|
|
51
|
-
validate_options = options.slice(:column, :name)
|
|
52
|
-
if ActiveRecord::VERSION::MAJOR >= 6
|
|
53
|
-
@migration.validate_foreign_key(from_table, to_table, **validate_options)
|
|
54
|
-
else
|
|
55
|
-
@migration.validate_foreign_key(from_table, validate_options.any? ? validate_options : to_table)
|
|
49
|
+
if !connection.foreign_key_exists?(from_table, to_table, **options.merge(validate: false))
|
|
50
|
+
@migration.add_foreign_key(from_table, to_table, *args, **options.merge(validate: false))
|
|
56
51
|
end
|
|
52
|
+
disable_transaction
|
|
53
|
+
@migration.validate_foreign_key(from_table, to_table, **options.slice(:column, :name))
|
|
57
54
|
end
|
|
58
55
|
dir.down do
|
|
59
|
-
|
|
60
|
-
if ActiveRecord::VERSION::MAJOR >= 6
|
|
61
|
-
@migration.remove_foreign_key(from_table, to_table, **remove_options)
|
|
62
|
-
else
|
|
63
|
-
@migration.remove_foreign_key(from_table, remove_options.any? ? remove_options : to_table)
|
|
64
|
-
end
|
|
56
|
+
@migration.remove_foreign_key(from_table, to_table, **options.slice(:column, :name))
|
|
65
57
|
end
|
|
66
58
|
end
|
|
67
59
|
end
|
|
@@ -69,7 +61,10 @@ module StrongMigrations
|
|
|
69
61
|
def safe_add_check_constraint(table, expression, *args, add_options, validate_options)
|
|
70
62
|
@migration.reversible do |dir|
|
|
71
63
|
dir.up do
|
|
72
|
-
|
|
64
|
+
# only skip invalid constraints
|
|
65
|
+
unless connection.check_constraints(table).any? { |c| c.options[:name] == validate_options[:name] && !c.options[:validate] }
|
|
66
|
+
@migration.add_check_constraint(table, expression, *args, **add_options)
|
|
67
|
+
end
|
|
73
68
|
disable_transaction
|
|
74
69
|
@migration.validate_check_constraint(table, **validate_options)
|
|
75
70
|
end
|
|
@@ -79,35 +74,60 @@ module StrongMigrations
|
|
|
79
74
|
end
|
|
80
75
|
end
|
|
81
76
|
|
|
82
|
-
def safe_change_column_null(
|
|
77
|
+
def safe_change_column_null(add_args, validate_args, change_args, remove_args, table, column, default, constraints)
|
|
83
78
|
@migration.reversible do |dir|
|
|
84
79
|
dir.up do
|
|
85
80
|
unless default.nil?
|
|
86
|
-
|
|
87
|
-
|
|
81
|
+
# TODO search for parent model if needed
|
|
82
|
+
if connection.pool != ActiveRecord::Base.connection_pool
|
|
83
|
+
raise_error :change_column_null,
|
|
84
|
+
code: backfill_code(table, column, default)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
model =
|
|
88
|
+
Class.new(ActiveRecord::Base) do
|
|
89
|
+
self.table_name = table
|
|
88
90
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
+
def self.to_s
|
|
92
|
+
"Backfill"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
update_sql =
|
|
97
|
+
model.connection_pool.with_connection do |c|
|
|
98
|
+
quoted_column = c.quote_column_name(column)
|
|
99
|
+
quoted_default = c.quote_default_expression(default, c.send(:column_for, table, column))
|
|
100
|
+
"#{quoted_column} = #{quoted_default}"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
@migration.say("Backfilling default")
|
|
91
104
|
disable_transaction
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
@migration.change_column_null(*change_args)
|
|
96
|
-
@migration.safety_assured do
|
|
97
|
-
@migration.execute(remove_code)
|
|
105
|
+
model.unscoped.in_batches(of: 10000) do |relation|
|
|
106
|
+
relation.where(column => nil).update_all(update_sql)
|
|
107
|
+
sleep(0.01)
|
|
98
108
|
end
|
|
99
109
|
end
|
|
110
|
+
|
|
111
|
+
add_options = add_args.extract_options!
|
|
112
|
+
validate_options = validate_args.extract_options!
|
|
113
|
+
remove_options = remove_args.extract_options!
|
|
114
|
+
|
|
115
|
+
# only skip invalid constraints
|
|
116
|
+
unless constraints.any? { |c| c.options[:name] == validate_options[:name] && !c.options[:validate] }
|
|
117
|
+
@migration.add_check_constraint(*add_args, **add_options)
|
|
118
|
+
end
|
|
119
|
+
disable_transaction
|
|
120
|
+
|
|
121
|
+
connection.begin_db_transaction
|
|
122
|
+
@migration.validate_check_constraint(*validate_args, **validate_options)
|
|
123
|
+
@migration.change_column_null(*change_args)
|
|
124
|
+
@migration.remove_check_constraint(*remove_args, **remove_options)
|
|
125
|
+
connection.commit_db_transaction
|
|
100
126
|
end
|
|
101
127
|
dir.down do
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
@migration.change_column_null(*down_args)
|
|
106
|
-
else
|
|
107
|
-
@migration.safety_assured do
|
|
108
|
-
@migration.execute(remove_code)
|
|
109
|
-
end
|
|
110
|
-
end
|
|
128
|
+
down_args = change_args.dup
|
|
129
|
+
down_args[2] = true
|
|
130
|
+
@migration.change_column_null(*down_args)
|
|
111
131
|
end
|
|
112
132
|
end
|
|
113
133
|
end
|
|
@@ -116,13 +136,13 @@ module StrongMigrations
|
|
|
116
136
|
# so just commit at start
|
|
117
137
|
def disable_transaction
|
|
118
138
|
if in_transaction? && !transaction_disabled
|
|
119
|
-
|
|
139
|
+
connection.commit_db_transaction
|
|
120
140
|
self.transaction_disabled = true
|
|
121
141
|
end
|
|
122
142
|
end
|
|
123
143
|
|
|
124
144
|
def in_transaction?
|
|
125
|
-
|
|
145
|
+
connection.open_transactions > 0
|
|
126
146
|
end
|
|
127
147
|
end
|
|
128
148
|
end
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
module StrongMigrations
|
|
2
2
|
module SchemaDumper
|
|
3
|
-
def initialize(connection,
|
|
3
|
+
def initialize(connection, ...)
|
|
4
4
|
return super unless StrongMigrations.alphabetize_schema
|
|
5
5
|
|
|
6
|
-
super(WrappedConnection.new(connection),
|
|
6
|
+
super(WrappedConnection.new(connection), ...)
|
|
7
7
|
end
|
|
8
8
|
end
|
|
9
9
|
|
|
@@ -14,8 +14,19 @@ module StrongMigrations
|
|
|
14
14
|
@connection = connection
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
def columns(
|
|
18
|
-
@connection.columns(
|
|
17
|
+
def columns(...)
|
|
18
|
+
@connection.columns(...).sort_by(&:name)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# forward private methods with send
|
|
22
|
+
# method_missing cannot tell how method was called
|
|
23
|
+
# this is not ideal, but other solutions have drawbacks
|
|
24
|
+
def send(name, ...)
|
|
25
|
+
if respond_to?(name, true)
|
|
26
|
+
super
|
|
27
|
+
else
|
|
28
|
+
@connection.send(name, ...)
|
|
29
|
+
end
|
|
19
30
|
end
|
|
20
31
|
end
|
|
21
32
|
end
|
data/lib/strong_migrations.rb
CHANGED
|
@@ -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, :transaction_timeout
|
|
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,16 +1,15 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: strong_migrations
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew Kane
|
|
8
8
|
- Bob Remeika
|
|
9
9
|
- David Waller
|
|
10
|
-
autorequire:
|
|
11
10
|
bindir: bin
|
|
12
11
|
cert_chain: []
|
|
13
|
-
date:
|
|
12
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
14
13
|
dependencies:
|
|
15
14
|
- !ruby/object:Gem::Dependency
|
|
16
15
|
name: activerecord
|
|
@@ -18,15 +17,14 @@ dependencies:
|
|
|
18
17
|
requirements:
|
|
19
18
|
- - ">="
|
|
20
19
|
- !ruby/object:Gem::Version
|
|
21
|
-
version: '
|
|
20
|
+
version: '7.2'
|
|
22
21
|
type: :runtime
|
|
23
22
|
prerelease: false
|
|
24
23
|
version_requirements: !ruby/object:Gem::Requirement
|
|
25
24
|
requirements:
|
|
26
25
|
- - ">="
|
|
27
26
|
- !ruby/object:Gem::Version
|
|
28
|
-
version: '
|
|
29
|
-
description:
|
|
27
|
+
version: '7.2'
|
|
30
28
|
email:
|
|
31
29
|
- andrew@ankane.org
|
|
32
30
|
- bob.remeika@gmail.com
|
|
@@ -47,9 +45,9 @@ files:
|
|
|
47
45
|
- lib/strong_migrations/adapters/postgresql_adapter.rb
|
|
48
46
|
- lib/strong_migrations/checker.rb
|
|
49
47
|
- lib/strong_migrations/checks.rb
|
|
50
|
-
- lib/strong_migrations/database_tasks.rb
|
|
51
48
|
- lib/strong_migrations/error_messages.rb
|
|
52
49
|
- lib/strong_migrations/migration.rb
|
|
50
|
+
- lib/strong_migrations/migration_context.rb
|
|
53
51
|
- lib/strong_migrations/migrator.rb
|
|
54
52
|
- lib/strong_migrations/railtie.rb
|
|
55
53
|
- lib/strong_migrations/safe_methods.rb
|
|
@@ -60,7 +58,6 @@ homepage: https://github.com/ankane/strong_migrations
|
|
|
60
58
|
licenses:
|
|
61
59
|
- MIT
|
|
62
60
|
metadata: {}
|
|
63
|
-
post_install_message:
|
|
64
61
|
rdoc_options: []
|
|
65
62
|
require_paths:
|
|
66
63
|
- lib
|
|
@@ -68,15 +65,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
68
65
|
requirements:
|
|
69
66
|
- - ">="
|
|
70
67
|
- !ruby/object:Gem::Version
|
|
71
|
-
version: '
|
|
68
|
+
version: '3.3'
|
|
72
69
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
73
70
|
requirements:
|
|
74
71
|
- - ">="
|
|
75
72
|
- !ruby/object:Gem::Version
|
|
76
73
|
version: '0'
|
|
77
74
|
requirements: []
|
|
78
|
-
rubygems_version:
|
|
79
|
-
signing_key:
|
|
75
|
+
rubygems_version: 4.0.10
|
|
80
76
|
specification_version: 4
|
|
81
77
|
summary: Catch unsafe migrations in development
|
|
82
78
|
test_files: []
|