strong_migrations 0.7.6 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/LICENSE.txt +1 -1
- data/README.md +44 -65
- data/lib/strong_migrations/checker.rb +33 -35
- data/lib/strong_migrations/database_tasks.rb +2 -1
- data/lib/strong_migrations/migration.rb +5 -0
- data/lib/strong_migrations/safe_methods.rb +5 -16
- data/lib/strong_migrations/version.rb +1 -1
- metadata +7 -77
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1f885cc0a8b0fb30e3bc4d000e82584fdf3850943563b4b426d3cfc39d70015c
|
4
|
+
data.tar.gz: d0dd29ea45ccc3b7c571e8bf03fa98c401d5489d1f5459488a0eff411cf6880e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c03a023d4c593d2868e524ca0f3e7ebd93e07e087e02f0d7090210b00fdb1fdc50d2abb74ab7df4131bddc6052fc5b9c10ce902ede8c708927476e40ad49d07f
|
7
|
+
data.tar.gz: 479e34db0101a15e1db1a593f01773da5665baa5cda3403ad32ea58dca00a0d0faee0ef813cf1975b7e9f99f1591e7f4f2be5c456c96fd6ae4633b48b6a0b3b4
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
## 0.8.0 (2022-02-09)
|
2
|
+
|
3
|
+
- Fixed error with versioned schema with Active Record 7.0.2+
|
4
|
+
- Dropped support for Ruby < 2.6 and Active Record < 5.2
|
5
|
+
|
6
|
+
## 0.7.9 (2021-12-15)
|
7
|
+
|
8
|
+
- Fixed error with multiple databases with Active Record 7
|
9
|
+
|
10
|
+
## 0.7.8 (2021-08-03)
|
11
|
+
|
12
|
+
- Fixed issue with `add_reference ..., foreign_key: {to_table: ...}` with `safe_by_default`
|
13
|
+
|
14
|
+
## 0.7.7 (2021-06-07)
|
15
|
+
|
16
|
+
- Removed timeouts and `auto_analyze` from schema load
|
17
|
+
|
1
18
|
## 0.7.6 (2021-01-17)
|
2
19
|
|
3
20
|
- Fixed `NOT NULL` constraint check for quoted columns
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -15,7 +15,7 @@ Supports for PostgreSQL, MySQL, and MariaDB
|
|
15
15
|
Add this line to your application’s Gemfile:
|
16
16
|
|
17
17
|
```ruby
|
18
|
-
gem
|
18
|
+
gem "strong_migrations"
|
19
19
|
```
|
20
20
|
|
21
21
|
And run:
|
@@ -43,7 +43,7 @@ end
|
|
43
43
|
|
44
44
|
Deploy the code, then wrap this step in a safety_assured { ... } block.
|
45
45
|
|
46
|
-
class RemoveColumn < ActiveRecord::Migration[
|
46
|
+
class RemoveColumn < ActiveRecord::Migration[7.0]
|
47
47
|
def change
|
48
48
|
safety_assured { remove_column :users, :name }
|
49
49
|
end
|
@@ -90,7 +90,7 @@ You can also add [custom checks](#custom-checks) or [disable specific checks](#d
|
|
90
90
|
Active Record caches database columns at runtime, so if you drop a column, it can cause exceptions until your app reboots.
|
91
91
|
|
92
92
|
```ruby
|
93
|
-
class RemoveSomeColumnFromUsers < ActiveRecord::Migration[
|
93
|
+
class RemoveSomeColumnFromUsers < ActiveRecord::Migration[7.0]
|
94
94
|
def change
|
95
95
|
remove_column :users, :some_column
|
96
96
|
end
|
@@ -111,7 +111,7 @@ end
|
|
111
111
|
3. Write a migration to remove the column (wrap in `safety_assured` block)
|
112
112
|
|
113
113
|
```ruby
|
114
|
-
class RemoveSomeColumnFromUsers < ActiveRecord::Migration[
|
114
|
+
class RemoveSomeColumnFromUsers < ActiveRecord::Migration[7.0]
|
115
115
|
def change
|
116
116
|
safety_assured { remove_column :users, :some_column }
|
117
117
|
end
|
@@ -119,6 +119,7 @@ end
|
|
119
119
|
```
|
120
120
|
|
121
121
|
4. Deploy and run migration
|
122
|
+
5. Remove the line added in step 1
|
122
123
|
|
123
124
|
### Adding a column with a default value
|
124
125
|
|
@@ -127,7 +128,7 @@ end
|
|
127
128
|
In earlier versions of Postgres, MySQL, and MariaDB, adding a column with a default value to an existing table causes the entire table to be rewritten. During this time, reads and writes are blocked in Postgres, and writes are blocked in MySQL and MariaDB.
|
128
129
|
|
129
130
|
```ruby
|
130
|
-
class AddSomeColumnToUsers < ActiveRecord::Migration[
|
131
|
+
class AddSomeColumnToUsers < ActiveRecord::Migration[7.0]
|
131
132
|
def change
|
132
133
|
add_column :users, :some_column, :text, default: "default_value"
|
133
134
|
end
|
@@ -141,7 +142,7 @@ In Postgres 11+, MySQL 8.0.12+, and MariaDB 10.3.2+, this no longer requires a t
|
|
141
142
|
Instead, add the column without a default value, then change the default.
|
142
143
|
|
143
144
|
```ruby
|
144
|
-
class AddSomeColumnToUsers < ActiveRecord::Migration[
|
145
|
+
class AddSomeColumnToUsers < ActiveRecord::Migration[7.0]
|
145
146
|
def up
|
146
147
|
add_column :users, :some_column, :text
|
147
148
|
change_column_default :users, :some_column, "default_value"
|
@@ -162,7 +163,7 @@ See the next section for how to backfill.
|
|
162
163
|
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/).
|
163
164
|
|
164
165
|
```ruby
|
165
|
-
class AddSomeColumnToUsers < ActiveRecord::Migration[
|
166
|
+
class AddSomeColumnToUsers < ActiveRecord::Migration[7.0]
|
166
167
|
def change
|
167
168
|
add_column :users, :some_column, :text
|
168
169
|
User.update_all some_column: "default_value"
|
@@ -177,7 +178,7 @@ Also, running a single query to update data can cause issues for large tables.
|
|
177
178
|
There are three keys to backfilling safely: batching, throttling, and running it outside a transaction. Use the Rails console or a separate migration with `disable_ddl_transaction!`.
|
178
179
|
|
179
180
|
```ruby
|
180
|
-
class BackfillSomeColumn < ActiveRecord::Migration[
|
181
|
+
class BackfillSomeColumn < ActiveRecord::Migration[7.0]
|
181
182
|
disable_ddl_transaction!
|
182
183
|
|
183
184
|
def up
|
@@ -196,7 +197,7 @@ end
|
|
196
197
|
Changing the type of a column causes the entire table to be rewritten. During this time, reads and writes are blocked in Postgres, and writes are blocked in MySQL and MariaDB.
|
197
198
|
|
198
199
|
```ruby
|
199
|
-
class ChangeSomeColumnType < ActiveRecord::Migration[
|
200
|
+
class ChangeSomeColumnType < ActiveRecord::Migration[7.0]
|
200
201
|
def change
|
201
202
|
change_column :users, :some_column, :new_type
|
202
203
|
end
|
@@ -235,7 +236,7 @@ A safer approach is to:
|
|
235
236
|
Renaming a column that’s in use will cause errors in your application.
|
236
237
|
|
237
238
|
```ruby
|
238
|
-
class RenameSomeColumn < ActiveRecord::Migration[
|
239
|
+
class RenameSomeColumn < ActiveRecord::Migration[7.0]
|
239
240
|
def change
|
240
241
|
rename_column :users, :some_column, :new_name
|
241
242
|
end
|
@@ -260,7 +261,7 @@ A safer approach is to:
|
|
260
261
|
Renaming a table that’s in use will cause errors in your application.
|
261
262
|
|
262
263
|
```ruby
|
263
|
-
class RenameUsersToCustomers < ActiveRecord::Migration[
|
264
|
+
class RenameUsersToCustomers < ActiveRecord::Migration[7.0]
|
264
265
|
def change
|
265
266
|
rename_table :users, :customers
|
266
267
|
end
|
@@ -285,7 +286,7 @@ A safer approach is to:
|
|
285
286
|
The `force` option can drop an existing table.
|
286
287
|
|
287
288
|
```ruby
|
288
|
-
class CreateUsers < ActiveRecord::Migration[
|
289
|
+
class CreateUsers < ActiveRecord::Migration[7.0]
|
289
290
|
def change
|
290
291
|
create_table :users, force: true do |t|
|
291
292
|
# ...
|
@@ -299,7 +300,7 @@ end
|
|
299
300
|
Create tables without the `force` option.
|
300
301
|
|
301
302
|
```ruby
|
302
|
-
class CreateUsers < ActiveRecord::Migration[
|
303
|
+
class CreateUsers < ActiveRecord::Migration[7.0]
|
303
304
|
def change
|
304
305
|
create_table :users do |t|
|
305
306
|
# ...
|
@@ -319,7 +320,7 @@ If you intend to drop an existing table, run `drop_table` first.
|
|
319
320
|
Adding a check constraint blocks reads and writes in Postgres and blocks writes in MySQL and MariaDB while every row is checked.
|
320
321
|
|
321
322
|
```ruby
|
322
|
-
class AddCheckConstraint < ActiveRecord::Migration[
|
323
|
+
class AddCheckConstraint < ActiveRecord::Migration[7.0]
|
323
324
|
def change
|
324
325
|
add_check_constraint :users, "price > 0", name: "price_check"
|
325
326
|
end
|
@@ -331,7 +332,7 @@ end
|
|
331
332
|
Add the check constraint without validating existing rows:
|
332
333
|
|
333
334
|
```ruby
|
334
|
-
class AddCheckConstraint < ActiveRecord::Migration[
|
335
|
+
class AddCheckConstraint < ActiveRecord::Migration[7.0]
|
335
336
|
def change
|
336
337
|
add_check_constraint :users, "price > 0", name: "price_check", validate: false
|
337
338
|
end
|
@@ -341,7 +342,7 @@ end
|
|
341
342
|
Then validate them in a separate migration.
|
342
343
|
|
343
344
|
```ruby
|
344
|
-
class ValidateCheckConstraint < ActiveRecord::Migration[
|
345
|
+
class ValidateCheckConstraint < ActiveRecord::Migration[7.0]
|
345
346
|
def change
|
346
347
|
validate_check_constraint :users, name: "price_check"
|
347
348
|
end
|
@@ -361,7 +362,7 @@ end
|
|
361
362
|
Setting `NOT NULL` on an existing column blocks reads and writes while every row is checked.
|
362
363
|
|
363
364
|
```ruby
|
364
|
-
class SetSomeColumnNotNull < ActiveRecord::Migration[
|
365
|
+
class SetSomeColumnNotNull < ActiveRecord::Migration[7.0]
|
365
366
|
def change
|
366
367
|
change_column_null :users, :some_column, false
|
367
368
|
end
|
@@ -375,7 +376,7 @@ Instead, add a check constraint.
|
|
375
376
|
For Rails 6.1, use:
|
376
377
|
|
377
378
|
```ruby
|
378
|
-
class SetSomeColumnNotNull < ActiveRecord::Migration[
|
379
|
+
class SetSomeColumnNotNull < ActiveRecord::Migration[7.0]
|
379
380
|
def change
|
380
381
|
add_check_constraint :users, "some_column IS NOT NULL", name: "users_some_column_null", validate: false
|
381
382
|
end
|
@@ -399,7 +400,7 @@ Then validate it in a separate migration. A `NOT NULL` check constraint is [func
|
|
399
400
|
For Rails 6.1, use:
|
400
401
|
|
401
402
|
```ruby
|
402
|
-
class ValidateSomeColumnNotNull < ActiveRecord::Migration[
|
403
|
+
class ValidateSomeColumnNotNull < ActiveRecord::Migration[7.0]
|
403
404
|
def change
|
404
405
|
validate_check_constraint :users, name: "users_some_column_null"
|
405
406
|
|
@@ -437,7 +438,7 @@ end
|
|
437
438
|
Strong Migrations can’t ensure safety for raw SQL statements. Make really sure that what you’re doing is safe, then use:
|
438
439
|
|
439
440
|
```ruby
|
440
|
-
class ExecuteSQL < ActiveRecord::Migration[
|
441
|
+
class ExecuteSQL < ActiveRecord::Migration[7.0]
|
441
442
|
def change
|
442
443
|
safety_assured { execute "..." }
|
443
444
|
end
|
@@ -453,7 +454,7 @@ end
|
|
453
454
|
In Postgres, adding an index non-concurrently blocks writes.
|
454
455
|
|
455
456
|
```ruby
|
456
|
-
class AddSomeIndexToUsers < ActiveRecord::Migration[
|
457
|
+
class AddSomeIndexToUsers < ActiveRecord::Migration[7.0]
|
457
458
|
def change
|
458
459
|
add_index :users, :some_column
|
459
460
|
end
|
@@ -465,7 +466,7 @@ end
|
|
465
466
|
Add indexes concurrently.
|
466
467
|
|
467
468
|
```ruby
|
468
|
-
class AddSomeIndexToUsers < ActiveRecord::Migration[
|
469
|
+
class AddSomeIndexToUsers < ActiveRecord::Migration[7.0]
|
469
470
|
disable_ddl_transaction!
|
470
471
|
|
471
472
|
def change
|
@@ -491,7 +492,7 @@ rails g index table column
|
|
491
492
|
Rails adds an index non-concurrently to references by default, which blocks writes in Postgres.
|
492
493
|
|
493
494
|
```ruby
|
494
|
-
class AddReferenceToUsers < ActiveRecord::Migration[
|
495
|
+
class AddReferenceToUsers < ActiveRecord::Migration[7.0]
|
495
496
|
def change
|
496
497
|
add_reference :users, :city
|
497
498
|
end
|
@@ -503,7 +504,7 @@ end
|
|
503
504
|
Make sure the index is added concurrently.
|
504
505
|
|
505
506
|
```ruby
|
506
|
-
class AddReferenceToUsers < ActiveRecord::Migration[
|
507
|
+
class AddReferenceToUsers < ActiveRecord::Migration[7.0]
|
507
508
|
disable_ddl_transaction!
|
508
509
|
|
509
510
|
def change
|
@@ -521,7 +522,7 @@ end
|
|
521
522
|
In Postgres, adding a foreign key blocks writes on both tables.
|
522
523
|
|
523
524
|
```ruby
|
524
|
-
class AddForeignKeyOnUsers < ActiveRecord::Migration[
|
525
|
+
class AddForeignKeyOnUsers < ActiveRecord::Migration[7.0]
|
525
526
|
def change
|
526
527
|
add_foreign_key :users, :orders
|
527
528
|
end
|
@@ -531,7 +532,7 @@ end
|
|
531
532
|
or
|
532
533
|
|
533
534
|
```ruby
|
534
|
-
class AddReferenceToUsers < ActiveRecord::Migration[
|
535
|
+
class AddReferenceToUsers < ActiveRecord::Migration[7.0]
|
535
536
|
def change
|
536
537
|
add_reference :users, :order, foreign_key: true
|
537
538
|
end
|
@@ -540,52 +541,26 @@ end
|
|
540
541
|
|
541
542
|
#### Good
|
542
543
|
|
543
|
-
Add the foreign key without validating existing rows
|
544
|
-
|
545
|
-
For Rails 5.2+, use:
|
544
|
+
Add the foreign key without validating existing rows:
|
546
545
|
|
547
546
|
```ruby
|
548
|
-
class AddForeignKeyOnUsers < ActiveRecord::Migration[
|
547
|
+
class AddForeignKeyOnUsers < ActiveRecord::Migration[7.0]
|
549
548
|
def change
|
550
549
|
add_foreign_key :users, :orders, validate: false
|
551
550
|
end
|
552
551
|
end
|
553
552
|
```
|
554
553
|
|
555
|
-
Then
|
554
|
+
Then validate them in a separate migration.
|
556
555
|
|
557
556
|
```ruby
|
558
|
-
class ValidateForeignKeyOnUsers < ActiveRecord::Migration[
|
557
|
+
class ValidateForeignKeyOnUsers < ActiveRecord::Migration[7.0]
|
559
558
|
def change
|
560
559
|
validate_foreign_key :users, :orders
|
561
560
|
end
|
562
561
|
end
|
563
562
|
```
|
564
563
|
|
565
|
-
For Rails < 5.2, use:
|
566
|
-
|
567
|
-
```ruby
|
568
|
-
class AddForeignKeyOnUsers < ActiveRecord::Migration[5.1]
|
569
|
-
def change
|
570
|
-
safety_assured do
|
571
|
-
execute 'ALTER TABLE "users" ADD CONSTRAINT "fk_rails_c1e9b98e31" FOREIGN KEY ("order_id") REFERENCES "orders" ("id") NOT VALID'
|
572
|
-
end
|
573
|
-
end
|
574
|
-
end
|
575
|
-
```
|
576
|
-
|
577
|
-
Then:
|
578
|
-
|
579
|
-
```ruby
|
580
|
-
class ValidateForeignKeyOnUsers < ActiveRecord::Migration[5.1]
|
581
|
-
def change
|
582
|
-
safety_assured do
|
583
|
-
execute 'ALTER TABLE "users" VALIDATE CONSTRAINT "fk_rails_c1e9b98e31"'
|
584
|
-
end
|
585
|
-
end
|
586
|
-
end
|
587
|
-
```
|
588
|
-
|
589
564
|
### Adding a json column
|
590
565
|
|
591
566
|
#### Bad
|
@@ -593,7 +568,7 @@ end
|
|
593
568
|
In Postgres, there’s no equality operator for the `json` column type, which can cause errors for existing `SELECT DISTINCT` queries in your application.
|
594
569
|
|
595
570
|
```ruby
|
596
|
-
class AddPropertiesToUsers < ActiveRecord::Migration[
|
571
|
+
class AddPropertiesToUsers < ActiveRecord::Migration[7.0]
|
597
572
|
def change
|
598
573
|
add_column :users, :properties, :json
|
599
574
|
end
|
@@ -605,7 +580,7 @@ end
|
|
605
580
|
Use `jsonb` instead.
|
606
581
|
|
607
582
|
```ruby
|
608
|
-
class AddPropertiesToUsers < ActiveRecord::Migration[
|
583
|
+
class AddPropertiesToUsers < ActiveRecord::Migration[7.0]
|
609
584
|
def change
|
610
585
|
add_column :users, :properties, :jsonb
|
611
586
|
end
|
@@ -619,7 +594,7 @@ end
|
|
619
594
|
Adding a non-unique index with more than three columns rarely improves performance.
|
620
595
|
|
621
596
|
```ruby
|
622
|
-
class AddSomeIndexToUsers < ActiveRecord::Migration[
|
597
|
+
class AddSomeIndexToUsers < ActiveRecord::Migration[7.0]
|
623
598
|
def change
|
624
599
|
add_index :users, [:a, :b, :c, :d]
|
625
600
|
end
|
@@ -631,7 +606,7 @@ end
|
|
631
606
|
Instead, start an index with columns that narrow down the results the most.
|
632
607
|
|
633
608
|
```ruby
|
634
|
-
class AddSomeIndexToUsers < ActiveRecord::Migration[
|
609
|
+
class AddSomeIndexToUsers < ActiveRecord::Migration[7.0]
|
635
610
|
def change
|
636
611
|
add_index :users, [:b, :d]
|
637
612
|
end
|
@@ -645,7 +620,7 @@ For Postgres, be sure to add them concurrently.
|
|
645
620
|
To mark a step in the migration as safe, despite using a method that might otherwise be dangerous, wrap it in a `safety_assured` block.
|
646
621
|
|
647
622
|
```ruby
|
648
|
-
class MySafeMigration < ActiveRecord::Migration[
|
623
|
+
class MySafeMigration < ActiveRecord::Migration[7.0]
|
649
624
|
def change
|
650
625
|
safety_assured { remove_column :users, :some_column }
|
651
626
|
end
|
@@ -818,11 +793,15 @@ StrongMigrations.auto_analyze = true
|
|
818
793
|
|
819
794
|
## Faster Migrations
|
820
795
|
|
821
|
-
Only dump the schema when adding a new migration. If you use Git,
|
796
|
+
Only dump the schema when adding a new migration. If you use Git, add to the end of your `Rakefile`:
|
822
797
|
|
823
|
-
```
|
824
|
-
|
825
|
-
|
798
|
+
```rb
|
799
|
+
task :faster_migrations do
|
800
|
+
ActiveRecord::Base.dump_schema_after_migration = Rails.env.development? &&
|
801
|
+
`git status db/migrate/ --porcelain`.present?
|
802
|
+
end
|
803
|
+
|
804
|
+
task "db:migrate": "faster_migrations"
|
826
805
|
```
|
827
806
|
|
828
807
|
## Schema Sanity
|
@@ -86,8 +86,7 @@ module StrongMigrations
|
|
86
86
|
options ||= {}
|
87
87
|
default = options[:default]
|
88
88
|
|
89
|
-
if !default.nil? && !
|
90
|
-
|
89
|
+
if !default.nil? && !add_column_default_safe?
|
91
90
|
if options[:null] == false
|
92
91
|
options = options.except(:null)
|
93
92
|
append = "
|
@@ -230,8 +229,10 @@ Then add the foreign key in separate migrations."
|
|
230
229
|
validate_code = constraint_str("ALTER TABLE %s VALIDATE CONSTRAINT %s", [table, constraint_name])
|
231
230
|
remove_code = constraint_str("ALTER TABLE %s DROP CONSTRAINT %s", [table, constraint_name])
|
232
231
|
|
232
|
+
constraint_methods = ar_version >= 6.1
|
233
|
+
|
233
234
|
validate_constraint_code =
|
234
|
-
if
|
235
|
+
if constraint_methods
|
235
236
|
String.new(command_str(:validate_check_constraint, [table, {name: constraint_name}]))
|
236
237
|
else
|
237
238
|
String.new(safety_assured_str(validate_code))
|
@@ -242,7 +243,7 @@ Then add the foreign key in separate migrations."
|
|
242
243
|
|
243
244
|
validate_constraint_code << "\n #{command_str(:change_column_null, change_args)}"
|
244
245
|
|
245
|
-
if
|
246
|
+
if constraint_methods
|
246
247
|
validate_constraint_code << "\n #{command_str(:remove_check_constraint, [table, {name: constraint_name}])}"
|
247
248
|
else
|
248
249
|
validate_constraint_code << "\n #{safety_assured_str(remove_code)}"
|
@@ -252,7 +253,7 @@ Then add the foreign key in separate migrations."
|
|
252
253
|
return safe_change_column_null(add_code, validate_code, change_args, remove_code) if StrongMigrations.safe_by_default
|
253
254
|
|
254
255
|
add_constraint_code =
|
255
|
-
if
|
256
|
+
if constraint_methods
|
256
257
|
# only quote when needed
|
257
258
|
expr_column = column.to_s =~ /\A[a-z0-9_]+\z/ ? column : connection.quote_column_name(column)
|
258
259
|
command_str(:add_check_constraint, [table, "#{expr_column} IS NOT NULL", {name: constraint_name, validate: false}])
|
@@ -275,32 +276,14 @@ Then add the foreign key in separate migrations."
|
|
275
276
|
from_table, to_table, options = args
|
276
277
|
options ||= {}
|
277
278
|
|
278
|
-
|
279
|
-
validate = options.fetch(:validate, true) || ar_version < 5.2
|
279
|
+
validate = options.fetch(:validate, true)
|
280
280
|
|
281
281
|
if postgresql? && validate
|
282
|
-
|
283
|
-
# fk name logic from rails
|
284
|
-
primary_key = options[:primary_key] || "id"
|
285
|
-
column = options[:column] || "#{to_table.to_s.singularize}_id"
|
286
|
-
hashed_identifier = Digest::SHA256.hexdigest("#{from_table}_#{column}_fk").first(10)
|
287
|
-
fk_name = options[:name] || "fk_rails_#{hashed_identifier}"
|
288
|
-
|
289
|
-
add_code = constraint_str("ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s) NOT VALID", [from_table, fk_name, column, to_table, primary_key])
|
290
|
-
validate_code = constraint_str("ALTER TABLE %s VALIDATE CONSTRAINT %s", [from_table, fk_name])
|
291
|
-
|
292
|
-
return safe_add_foreign_key_code(from_table, to_table, add_code, validate_code) if StrongMigrations.safe_by_default
|
282
|
+
return safe_add_foreign_key(from_table, to_table, options) if StrongMigrations.safe_by_default
|
293
283
|
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
else
|
298
|
-
return safe_add_foreign_key(from_table, to_table, options) if StrongMigrations.safe_by_default
|
299
|
-
|
300
|
-
raise_error :add_foreign_key,
|
301
|
-
add_foreign_key_code: command_str("add_foreign_key", [from_table, to_table, options.merge(validate: false)]),
|
302
|
-
validate_foreign_key_code: command_str("validate_foreign_key", [from_table, to_table])
|
303
|
-
end
|
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])
|
304
287
|
end
|
305
288
|
when :validate_foreign_key
|
306
289
|
if postgresql? && writes_blocked?
|
@@ -340,11 +323,7 @@ Then add the foreign key in separate migrations."
|
|
340
323
|
|
341
324
|
# outdated statistics + a new index can hurt performance of existing queries
|
342
325
|
if StrongMigrations.auto_analyze && direction == :up && method == :add_index
|
343
|
-
|
344
|
-
connection.execute "ANALYZE #{connection.quote_table_name(args[0].to_s)}"
|
345
|
-
elsif mariadb? || mysql?
|
346
|
-
connection.execute "ANALYZE TABLE #{connection.quote_table_name(args[0].to_s)}"
|
347
|
-
end
|
326
|
+
analyze_table(args[0])
|
348
327
|
end
|
349
328
|
|
350
329
|
result
|
@@ -396,8 +375,7 @@ Then add the foreign key in separate migrations."
|
|
396
375
|
end
|
397
376
|
|
398
377
|
def safe?
|
399
|
-
@safe || ENV["SAFETY_ASSURED"] ||
|
400
|
-
(direction == :down && !StrongMigrations.check_down) || version_safe?
|
378
|
+
@safe || ENV["SAFETY_ASSURED"] || (direction == :down && !StrongMigrations.check_down) || version_safe?
|
401
379
|
end
|
402
380
|
|
403
381
|
def version_safe?
|
@@ -508,6 +486,14 @@ Then add the foreign key in separate migrations."
|
|
508
486
|
end
|
509
487
|
end
|
510
488
|
|
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)}"
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
511
497
|
def constraints(table_name)
|
512
498
|
query = <<~SQL
|
513
499
|
SELECT
|
@@ -595,5 +581,17 @@ Then add the foreign key in separate migrations."
|
|
595
581
|
def new_table?(table)
|
596
582
|
@new_tables.include?(table.to_s)
|
597
583
|
end
|
584
|
+
|
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
|
595
|
+
end
|
598
596
|
end
|
599
597
|
end
|
@@ -7,6 +7,11 @@ 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)
|
14
|
+
|
10
15
|
strong_migrations_checker.perform(method, *args) do
|
11
16
|
super
|
12
17
|
end
|
@@ -30,7 +30,11 @@ module StrongMigrations
|
|
30
30
|
(ActiveRecord::Base.pluralize_table_names ? reference.to_s.pluralize : reference).to_sym
|
31
31
|
end
|
32
32
|
|
33
|
-
|
33
|
+
if reference
|
34
|
+
@migration.add_foreign_key(table, name, column: "#{reference}_id")
|
35
|
+
else
|
36
|
+
@migration.add_foreign_key(table, name)
|
37
|
+
end
|
34
38
|
end
|
35
39
|
end
|
36
40
|
dir.down do
|
@@ -52,21 +56,6 @@ module StrongMigrations
|
|
52
56
|
end
|
53
57
|
end
|
54
58
|
|
55
|
-
def safe_add_foreign_key_code(from_table, to_table, add_code, validate_code)
|
56
|
-
@migration.reversible do |dir|
|
57
|
-
dir.up do
|
58
|
-
@migration.safety_assured do
|
59
|
-
@migration.execute(add_code)
|
60
|
-
disable_transaction
|
61
|
-
@migration.execute(validate_code)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
dir.down do
|
65
|
-
@migration.remove_foreign_key(from_table, to_table)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
59
|
def safe_add_check_constraint(table, expression, add_options, validate_options)
|
71
60
|
@migration.reversible do |dir|
|
72
61
|
dir.up do
|
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.
|
4
|
+
version: 0.8.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:
|
13
|
+
date: 2022-02-10 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
@@ -18,87 +18,17 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - ">="
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: '5'
|
21
|
+
version: '5.2'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
requirements:
|
26
26
|
- - ">="
|
27
27
|
- !ruby/object:Gem::Version
|
28
|
-
version: '5'
|
29
|
-
- !ruby/object:Gem::Dependency
|
30
|
-
name: bundler
|
31
|
-
requirement: !ruby/object:Gem::Requirement
|
32
|
-
requirements:
|
33
|
-
- - ">="
|
34
|
-
- !ruby/object:Gem::Version
|
35
|
-
version: '0'
|
36
|
-
type: :development
|
37
|
-
prerelease: false
|
38
|
-
version_requirements: !ruby/object:Gem::Requirement
|
39
|
-
requirements:
|
40
|
-
- - ">="
|
41
|
-
- !ruby/object:Gem::Version
|
42
|
-
version: '0'
|
43
|
-
- !ruby/object:Gem::Dependency
|
44
|
-
name: rake
|
45
|
-
requirement: !ruby/object:Gem::Requirement
|
46
|
-
requirements:
|
47
|
-
- - ">="
|
48
|
-
- !ruby/object:Gem::Version
|
49
|
-
version: '0'
|
50
|
-
type: :development
|
51
|
-
prerelease: false
|
52
|
-
version_requirements: !ruby/object:Gem::Requirement
|
53
|
-
requirements:
|
54
|
-
- - ">="
|
55
|
-
- !ruby/object:Gem::Version
|
56
|
-
version: '0'
|
57
|
-
- !ruby/object:Gem::Dependency
|
58
|
-
name: minitest
|
59
|
-
requirement: !ruby/object:Gem::Requirement
|
60
|
-
requirements:
|
61
|
-
- - ">="
|
62
|
-
- !ruby/object:Gem::Version
|
63
|
-
version: '0'
|
64
|
-
type: :development
|
65
|
-
prerelease: false
|
66
|
-
version_requirements: !ruby/object:Gem::Requirement
|
67
|
-
requirements:
|
68
|
-
- - ">="
|
69
|
-
- !ruby/object:Gem::Version
|
70
|
-
version: '0'
|
71
|
-
- !ruby/object:Gem::Dependency
|
72
|
-
name: pg
|
73
|
-
requirement: !ruby/object:Gem::Requirement
|
74
|
-
requirements:
|
75
|
-
- - ">="
|
76
|
-
- !ruby/object:Gem::Version
|
77
|
-
version: '0'
|
78
|
-
type: :development
|
79
|
-
prerelease: false
|
80
|
-
version_requirements: !ruby/object:Gem::Requirement
|
81
|
-
requirements:
|
82
|
-
- - ">="
|
83
|
-
- !ruby/object:Gem::Version
|
84
|
-
version: '0'
|
85
|
-
- !ruby/object:Gem::Dependency
|
86
|
-
name: mysql2
|
87
|
-
requirement: !ruby/object:Gem::Requirement
|
88
|
-
requirements:
|
89
|
-
- - ">="
|
90
|
-
- !ruby/object:Gem::Version
|
91
|
-
version: '0'
|
92
|
-
type: :development
|
93
|
-
prerelease: false
|
94
|
-
version_requirements: !ruby/object:Gem::Requirement
|
95
|
-
requirements:
|
96
|
-
- - ">="
|
97
|
-
- !ruby/object:Gem::Version
|
98
|
-
version: '0'
|
28
|
+
version: '5.2'
|
99
29
|
description:
|
100
30
|
email:
|
101
|
-
- andrew@
|
31
|
+
- andrew@ankane.org
|
102
32
|
- bob.remeika@gmail.com
|
103
33
|
executables: []
|
104
34
|
extensions: []
|
@@ -131,14 +61,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
131
61
|
requirements:
|
132
62
|
- - ">="
|
133
63
|
- !ruby/object:Gem::Version
|
134
|
-
version: '2.
|
64
|
+
version: '2.6'
|
135
65
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
136
66
|
requirements:
|
137
67
|
- - ">="
|
138
68
|
- !ruby/object:Gem::Version
|
139
69
|
version: '0'
|
140
70
|
requirements: []
|
141
|
-
rubygems_version: 3.
|
71
|
+
rubygems_version: 3.3.3
|
142
72
|
signing_key:
|
143
73
|
specification_version: 4
|
144
74
|
summary: Catch unsafe migrations in development
|