strong_migrations 1.8.0 → 2.3.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: f6462f35144a092664bb7456db2a1ad402695c6b5b1ba7001c1f1f977ea88d3e
4
- data.tar.gz: fc71c84ae237b8dbbd3413af7b47ab4fa9962dcbbdd6b3e99b9edd816d3ae009
3
+ metadata.gz: 3538811c535186c2757c2e92a56ec753c77842a99728a727aabe323dd8852182
4
+ data.tar.gz: 6d33a69582e57e3035d7ef540894ce0020957aef8d4e081e693552615ac9af8b
5
5
  SHA512:
6
- metadata.gz: 2c4e5bf21e4bb42046fc2d4a530e40d9fc5f843db7c630779789934ec1a47965398e6b637707dc6a7106eb339ef26231ecfea73c312fd9a04f66f96504fcdc6a
7
- data.tar.gz: 542be70454fa38a8ad0c29e6bedcf670e54323ff8d9ae8f5eb59756476bbbaca6092a15f09c54163714e3db83261a8554e1fe51b00da8ad05ca7667a4612c3c8
6
+ metadata.gz: 50a4b8ea0d0677bc0d5a3fa740f9f2e8b00acee0e673abaf7197c0c89540165ecb0cad913ed4244b61e3ac23342d4c44b8209991dea3841ee6dd7d7fbef827e3
7
+ data.tar.gz: fd6d44273d6365ca9e33a246e072a24da93191bfc73b5d425df22c0b3b6523ce218d6aa6360dd6a8fcbcf88b5a06bf41c7221ccdceb69f26b734510cd38876f7
data/CHANGELOG.md CHANGED
@@ -1,3 +1,41 @@
1
+ ## 2.3.0 (2025-04-03)
2
+
3
+ - Added check for `change_column` for columns with check constraints with Postgres
4
+
5
+ ## 2.2.1 (2025-03-21)
6
+
7
+ - Added support for `change_column_null` with default value with `safe_by_default` option
8
+ - Improved backfill instructions
9
+ - Fixed `safe_by_default` applying to migrations before `start_after`
10
+
11
+ ## 2.2.0 (2025-02-01)
12
+
13
+ - Fixed constraint name for long table and column names with `change_column_null`
14
+ - Dropped support for Active Record < 7
15
+
16
+ ## 2.1.0 (2024-11-08)
17
+
18
+ - Added `skip_database` method
19
+ - Added experimental `remove_invalid_indexes` option
20
+ - Added warning for unsupported adapters
21
+ - Improved output for `db:forward`, `db:rollback`, `db:migrate:up`, and `db:migrate:down`
22
+ - Made operations more retriable with `safe_by_default`
23
+
24
+ ## 2.0.2 (2024-10-30)
25
+
26
+ - Fixed migrations not running with Active Record 8 rc2
27
+
28
+ ## 2.0.1 (2024-10-14)
29
+
30
+ - Fixed issue with `alphabetize_schema` and virtual columns
31
+
32
+ ## 2.0.0 (2024-06-28)
33
+
34
+ - Improved install generator for Trilogy
35
+ - Fixed charset check for MariaDB 11.4
36
+ - Dropped support for Ruby < 3.1 and Active Record < 6.1
37
+ - Dropped support for Postgres < 12, MySQL < 8.0, and MariaDB < 10.5
38
+
1
39
  ## 1.8.0 (2024-03-11)
2
40
 
3
41
  - Added check for `add_column` with auto-incrementing columns
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013 Bob Remeika and David Waller, 2015-2024 Andrew Kane
1
+ Copyright (c) 2013 Bob Remeika and David Waller, 2015-2025 Andrew Kane
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -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[7.1]
46
+ class RemoveColumn < ActiveRecord::Migration[8.0]
47
47
  def change
48
48
  safety_assured { remove_column :users, :name }
49
49
  end
@@ -64,7 +64,7 @@ Potentially dangerous operations:
64
64
  - [renaming a column](#renaming-a-column)
65
65
  - [renaming a table](#renaming-a-table)
66
66
  - [creating a table with the force option](#creating-a-table-with-the-force-option)
67
- - [adding an auto-incrementing column](#adding-an-auto-incrementing-column) [unreleased]
67
+ - [adding an auto-incrementing column](#adding-an-auto-incrementing-column)
68
68
  - [adding a stored generated column](#adding-a-stored-generated-column)
69
69
  - [adding a check constraint](#adding-a-check-constraint)
70
70
  - [executing SQL directly](#executing-SQL-directly)
@@ -79,7 +79,7 @@ Postgres-specific checks:
79
79
  - [adding an exclusion constraint](#adding-an-exclusion-constraint)
80
80
  - [adding a json column](#adding-a-json-column)
81
81
  - [setting NOT NULL on an existing column](#setting-not-null-on-an-existing-column)
82
- - [adding a column with a default value](#adding-a-column-with-a-default-value)
82
+ - [adding a column with a volatile default value](#adding-a-column-with-a-volatile-default-value)
83
83
 
84
84
  Config-specific checks:
85
85
 
@@ -98,7 +98,7 @@ You can also add [custom checks](#custom-checks) or [disable specific checks](#d
98
98
  Active Record caches database columns at runtime, so if you drop a column, it can cause exceptions until your app reboots.
99
99
 
100
100
  ```ruby
101
- class RemoveSomeColumnFromUsers < ActiveRecord::Migration[7.1]
101
+ class RemoveSomeColumnFromUsers < ActiveRecord::Migration[8.0]
102
102
  def change
103
103
  remove_column :users, :some_column
104
104
  end
@@ -119,7 +119,7 @@ end
119
119
  3. Write a migration to remove the column (wrap in `safety_assured` block)
120
120
 
121
121
  ```ruby
122
- class RemoveSomeColumnFromUsers < ActiveRecord::Migration[7.1]
122
+ class RemoveSomeColumnFromUsers < ActiveRecord::Migration[8.0]
123
123
  def change
124
124
  safety_assured { remove_column :users, :some_column }
125
125
  end
@@ -136,7 +136,7 @@ end
136
136
  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.
137
137
 
138
138
  ```ruby
139
- class ChangeSomeColumnType < ActiveRecord::Migration[7.1]
139
+ class ChangeSomeColumnType < ActiveRecord::Migration[8.0]
140
140
  def change
141
141
  change_column :users, :some_column, :new_type
142
142
  end
@@ -182,7 +182,7 @@ A safer approach is to:
182
182
  Renaming a column that’s in use will cause errors in your application.
183
183
 
184
184
  ```ruby
185
- class RenameSomeColumn < ActiveRecord::Migration[7.1]
185
+ class RenameSomeColumn < ActiveRecord::Migration[8.0]
186
186
  def change
187
187
  rename_column :users, :some_column, :new_name
188
188
  end
@@ -207,7 +207,7 @@ A safer approach is to:
207
207
  Renaming a table that’s in use will cause errors in your application.
208
208
 
209
209
  ```ruby
210
- class RenameUsersToCustomers < ActiveRecord::Migration[7.1]
210
+ class RenameUsersToCustomers < ActiveRecord::Migration[8.0]
211
211
  def change
212
212
  rename_table :users, :customers
213
213
  end
@@ -232,7 +232,7 @@ A safer approach is to:
232
232
  The `force` option can drop an existing table.
233
233
 
234
234
  ```ruby
235
- class CreateUsers < ActiveRecord::Migration[7.1]
235
+ class CreateUsers < ActiveRecord::Migration[8.0]
236
236
  def change
237
237
  create_table :users, force: true do |t|
238
238
  # ...
@@ -246,7 +246,7 @@ end
246
246
  Create tables without the `force` option.
247
247
 
248
248
  ```ruby
249
- class CreateUsers < ActiveRecord::Migration[7.1]
249
+ class CreateUsers < ActiveRecord::Migration[8.0]
250
250
  def change
251
251
  create_table :users do |t|
252
252
  # ...
@@ -264,7 +264,7 @@ If you intend to drop an existing table, run `drop_table` first.
264
264
  Adding an auto-incrementing column (`serial`/`bigserial` in Postgres and `AUTO_INCREMENT` in MySQL and MariaDB) 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.
265
265
 
266
266
  ```ruby
267
- class AddIdToCitiesUsers < ActiveRecord::Migration[7.1]
267
+ class AddIdToCitiesUsers < ActiveRecord::Migration[8.0]
268
268
  def change
269
269
  add_column :cities_users, :id, :primary_key
270
270
  end
@@ -284,7 +284,7 @@ Create a new table and migrate the data with the same steps as [renaming a table
284
284
  Adding a stored generated 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.
285
285
 
286
286
  ```ruby
287
- class AddSomeColumnToUsers < ActiveRecord::Migration[7.1]
287
+ class AddSomeColumnToUsers < ActiveRecord::Migration[8.0]
288
288
  def change
289
289
  add_column :users, :some_column, :virtual, type: :string, as: "...", stored: true
290
290
  end
@@ -304,7 +304,7 @@ Add a non-generated column and use callbacks or triggers instead (or a virtual g
304
304
  Adding a check constraint blocks reads and writes in Postgres and blocks writes in MySQL and MariaDB while every row is checked.
305
305
 
306
306
  ```ruby
307
- class AddCheckConstraint < ActiveRecord::Migration[7.1]
307
+ class AddCheckConstraint < ActiveRecord::Migration[8.0]
308
308
  def change
309
309
  add_check_constraint :users, "price > 0", name: "price_check"
310
310
  end
@@ -316,7 +316,7 @@ end
316
316
  Add the check constraint without validating existing rows:
317
317
 
318
318
  ```ruby
319
- class AddCheckConstraint < ActiveRecord::Migration[7.1]
319
+ class AddCheckConstraint < ActiveRecord::Migration[8.0]
320
320
  def change
321
321
  add_check_constraint :users, "price > 0", name: "price_check", validate: false
322
322
  end
@@ -326,7 +326,7 @@ end
326
326
  Then validate them in a separate migration.
327
327
 
328
328
  ```ruby
329
- class ValidateCheckConstraint < ActiveRecord::Migration[7.1]
329
+ class ValidateCheckConstraint < ActiveRecord::Migration[8.0]
330
330
  def change
331
331
  validate_check_constraint :users, name: "price_check"
332
332
  end
@@ -342,7 +342,7 @@ end
342
342
  Strong Migrations can’t ensure safety for raw SQL statements. Make really sure that what you’re doing is safe, then use:
343
343
 
344
344
  ```ruby
345
- class ExecuteSQL < ActiveRecord::Migration[7.1]
345
+ class ExecuteSQL < ActiveRecord::Migration[8.0]
346
346
  def change
347
347
  safety_assured { execute "..." }
348
348
  end
@@ -351,12 +351,14 @@ 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/).
357
359
 
358
360
  ```ruby
359
- class AddSomeColumnToUsers < ActiveRecord::Migration[7.1]
361
+ class AddSomeColumnToUsers < ActiveRecord::Migration[8.0]
360
362
  def change
361
363
  add_column :users, :some_column, :text
362
364
  User.update_all some_column: "default_value"
@@ -371,18 +373,20 @@ Also, running a single query to update data can cause issues for large tables.
371
373
  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!`.
372
374
 
373
375
  ```ruby
374
- class BackfillSomeColumn < ActiveRecord::Migration[7.1]
376
+ class BackfillSomeColumn < ActiveRecord::Migration[8.0]
375
377
  disable_ddl_transaction!
376
378
 
377
379
  def up
378
- User.unscoped.in_batches do |relation|
379
- relation.update_all some_column: "default_value"
380
+ User.unscoped.in_batches(of: 10000) do |relation|
381
+ relation.where(some_column: nil).update_all some_column: "default_value"
380
382
  sleep(0.01) # throttle
381
383
  end
382
384
  end
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
@@ -392,7 +396,7 @@ end
392
396
  In Postgres, adding an index non-concurrently blocks writes.
393
397
 
394
398
  ```ruby
395
- class AddSomeIndexToUsers < ActiveRecord::Migration[7.1]
399
+ class AddSomeIndexToUsers < ActiveRecord::Migration[8.0]
396
400
  def change
397
401
  add_index :users, :some_column
398
402
  end
@@ -404,7 +408,7 @@ end
404
408
  Add indexes concurrently.
405
409
 
406
410
  ```ruby
407
- class AddSomeIndexToUsers < ActiveRecord::Migration[7.1]
411
+ class AddSomeIndexToUsers < ActiveRecord::Migration[8.0]
408
412
  disable_ddl_transaction!
409
413
 
410
414
  def change
@@ -430,7 +434,7 @@ rails g index table column
430
434
  Rails adds an index non-concurrently to references by default, which blocks writes in Postgres.
431
435
 
432
436
  ```ruby
433
- class AddReferenceToUsers < ActiveRecord::Migration[7.1]
437
+ class AddReferenceToUsers < ActiveRecord::Migration[8.0]
434
438
  def change
435
439
  add_reference :users, :city
436
440
  end
@@ -442,7 +446,7 @@ end
442
446
  Make sure the index is added concurrently.
443
447
 
444
448
  ```ruby
445
- class AddReferenceToUsers < ActiveRecord::Migration[7.1]
449
+ class AddReferenceToUsers < ActiveRecord::Migration[8.0]
446
450
  disable_ddl_transaction!
447
451
 
448
452
  def change
@@ -460,7 +464,7 @@ end
460
464
  In Postgres, adding a foreign key blocks writes on both tables.
461
465
 
462
466
  ```ruby
463
- class AddForeignKeyOnUsers < ActiveRecord::Migration[7.1]
467
+ class AddForeignKeyOnUsers < ActiveRecord::Migration[8.0]
464
468
  def change
465
469
  add_foreign_key :users, :orders
466
470
  end
@@ -470,7 +474,7 @@ end
470
474
  or
471
475
 
472
476
  ```ruby
473
- class AddReferenceToUsers < ActiveRecord::Migration[7.1]
477
+ class AddReferenceToUsers < ActiveRecord::Migration[8.0]
474
478
  def change
475
479
  add_reference :users, :order, foreign_key: true
476
480
  end
@@ -482,7 +486,7 @@ end
482
486
  Add the foreign key without validating existing rows:
483
487
 
484
488
  ```ruby
485
- class AddForeignKeyOnUsers < ActiveRecord::Migration[7.1]
489
+ class AddForeignKeyOnUsers < ActiveRecord::Migration[8.0]
486
490
  def change
487
491
  add_foreign_key :users, :orders, validate: false
488
492
  end
@@ -492,7 +496,7 @@ end
492
496
  Then validate them in a separate migration.
493
497
 
494
498
  ```ruby
495
- class ValidateForeignKeyOnUsers < ActiveRecord::Migration[7.1]
499
+ class ValidateForeignKeyOnUsers < ActiveRecord::Migration[8.0]
496
500
  def change
497
501
  validate_foreign_key :users, :orders
498
502
  end
@@ -506,7 +510,7 @@ end
506
510
  In Postgres, adding a unique constraint creates a unique index, which blocks reads and writes.
507
511
 
508
512
  ```ruby
509
- class AddUniqueContraint < ActiveRecord::Migration[7.1]
513
+ class AddUniqueConstraint < ActiveRecord::Migration[8.0]
510
514
  def change
511
515
  add_unique_constraint :users, :some_column
512
516
  end
@@ -518,7 +522,7 @@ end
518
522
  Create a unique index concurrently, then use it for the constraint.
519
523
 
520
524
  ```ruby
521
- class AddUniqueContraint < ActiveRecord::Migration[7.1]
525
+ class AddUniqueConstraint < ActiveRecord::Migration[8.0]
522
526
  disable_ddl_transaction!
523
527
 
524
528
  def up
@@ -539,7 +543,7 @@ end
539
543
  In Postgres, adding an exclusion constraint blocks reads and writes while every row is checked.
540
544
 
541
545
  ```ruby
542
- class AddExclusionContraint < ActiveRecord::Migration[7.1]
546
+ class AddExclusionConstraint < ActiveRecord::Migration[8.0]
543
547
  def change
544
548
  add_exclusion_constraint :users, "number WITH =", using: :gist
545
549
  end
@@ -557,7 +561,7 @@ end
557
561
  In Postgres, there’s no equality operator for the `json` column type, which can cause errors for existing `SELECT DISTINCT` queries in your application.
558
562
 
559
563
  ```ruby
560
- class AddPropertiesToUsers < ActiveRecord::Migration[7.1]
564
+ class AddPropertiesToUsers < ActiveRecord::Migration[8.0]
561
565
  def change
562
566
  add_column :users, :properties, :json
563
567
  end
@@ -569,7 +573,7 @@ end
569
573
  Use `jsonb` instead.
570
574
 
571
575
  ```ruby
572
- class AddPropertiesToUsers < ActiveRecord::Migration[7.1]
576
+ class AddPropertiesToUsers < ActiveRecord::Migration[8.0]
573
577
  def change
574
578
  add_column :users, :properties, :jsonb
575
579
  end
@@ -585,7 +589,7 @@ end
585
589
  In Postgres, setting `NOT NULL` on an existing column blocks reads and writes while every row is checked.
586
590
 
587
591
  ```ruby
588
- class SetSomeColumnNotNull < ActiveRecord::Migration[7.1]
592
+ class SetSomeColumnNotNull < ActiveRecord::Migration[8.0]
589
593
  def change
590
594
  change_column_null :users, :some_column, false
591
595
  end
@@ -596,87 +600,54 @@ end
596
600
 
597
601
  Instead, add a check constraint.
598
602
 
599
- For Rails 6.1+, use:
600
-
601
603
  ```ruby
602
- class SetSomeColumnNotNull < ActiveRecord::Migration[7.1]
604
+ class SetSomeColumnNotNull < ActiveRecord::Migration[8.0]
603
605
  def change
604
606
  add_check_constraint :users, "some_column IS NOT NULL", name: "users_some_column_null", validate: false
605
607
  end
606
608
  end
607
609
  ```
608
610
 
609
- For Rails < 6.1, use:
610
-
611
- ```ruby
612
- class SetSomeColumnNotNull < ActiveRecord::Migration[6.0]
613
- def change
614
- safety_assured do
615
- execute 'ALTER TABLE "users" ADD CONSTRAINT "users_some_column_null" CHECK ("some_column" IS NOT NULL) NOT VALID'
616
- end
617
- end
618
- end
619
- ```
620
-
621
- Then validate it in a separate migration. A `NOT NULL` check constraint is [functionally equivalent](https://medium.com/doctolib/adding-a-not-null-constraint-on-pg-faster-with-minimal-locking-38b2c00c4d1c) to setting `NOT NULL` on the column (but it won’t show up in `schema.rb` in Rails < 6.1). In Postgres 12+, once the check constraint is validated, you can safely set `NOT NULL` on the column and drop the check constraint.
622
-
623
- For Rails 6.1+, use:
611
+ Then validate it in a separate migration. Once the check constraint is validated, you can safely set `NOT NULL` on the column and drop the check constraint.
624
612
 
625
613
  ```ruby
626
- class ValidateSomeColumnNotNull < ActiveRecord::Migration[7.1]
627
- def change
614
+ class ValidateSomeColumnNotNull < ActiveRecord::Migration[8.0]
615
+ def up
628
616
  validate_check_constraint :users, name: "users_some_column_null"
629
-
630
- # in Postgres 12+, you can then safely set NOT NULL on the column
631
617
  change_column_null :users, :some_column, false
632
618
  remove_check_constraint :users, name: "users_some_column_null"
633
619
  end
634
- end
635
- ```
636
-
637
- For Rails < 6.1, use:
638
-
639
- ```ruby
640
- class ValidateSomeColumnNotNull < ActiveRecord::Migration[6.0]
641
- def change
642
- safety_assured do
643
- execute 'ALTER TABLE "users" VALIDATE CONSTRAINT "users_some_column_null"'
644
- end
645
620
 
646
- # in Postgres 12+, you can then safely set NOT NULL on the column
647
- change_column_null :users, :some_column, false
648
- safety_assured do
649
- execute 'ALTER TABLE "users" DROP CONSTRAINT "users_some_column_null"'
650
- end
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
651
624
  end
652
625
  end
653
626
  ```
654
627
 
655
- ### Adding a column with a default value
628
+ ### Adding a column with a volatile default value
656
629
 
657
630
  #### Bad
658
631
 
659
- In earlier versions of Postgres, 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.
632
+ Adding a column with a volatile default value to an existing table causes the entire table to be rewritten. During this time, reads and writes are blocked.
660
633
 
661
634
  ```ruby
662
- class AddSomeColumnToUsers < ActiveRecord::Migration[7.1]
635
+ class AddSomeColumnToUsers < ActiveRecord::Migration[8.0]
663
636
  def change
664
- add_column :users, :some_column, :text, default: "default_value"
637
+ add_column :users, :some_column, :uuid, default: "gen_random_uuid()"
665
638
  end
666
639
  end
667
640
  ```
668
641
 
669
- In Postgres 11+, this no longer requires a table rewrite and is safe (except for volatile functions like `gen_random_uuid()`).
670
-
671
642
  #### Good
672
643
 
673
644
  Instead, add the column without a default value, then change the default.
674
645
 
675
646
  ```ruby
676
- class AddSomeColumnToUsers < ActiveRecord::Migration[7.1]
647
+ class AddSomeColumnToUsers < ActiveRecord::Migration[8.0]
677
648
  def up
678
- add_column :users, :some_column, :text
679
- change_column_default :users, :some_column, "default_value"
649
+ add_column :users, :some_column, :uuid
650
+ change_column_default :users, :some_column, from: nil, to: "gen_random_uuid()"
680
651
  end
681
652
 
682
653
  def down
@@ -724,7 +695,7 @@ config.active_record.partial_inserts = false
724
695
  Adding a non-unique index with more than three columns rarely improves performance.
725
696
 
726
697
  ```ruby
727
- class AddSomeIndexToUsers < ActiveRecord::Migration[7.1]
698
+ class AddSomeIndexToUsers < ActiveRecord::Migration[8.0]
728
699
  def change
729
700
  add_index :users, [:a, :b, :c, :d]
730
701
  end
@@ -736,9 +707,9 @@ end
736
707
  Instead, start an index with columns that narrow down the results the most.
737
708
 
738
709
  ```ruby
739
- class AddSomeIndexToUsers < ActiveRecord::Migration[7.1]
710
+ class AddSomeIndexToUsers < ActiveRecord::Migration[8.0]
740
711
  def change
741
- add_index :users, [:b, :d]
712
+ add_index :users, [:d, :b]
742
713
  end
743
714
  end
744
715
  ```
@@ -750,7 +721,7 @@ For Postgres, be sure to add them concurrently.
750
721
  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.
751
722
 
752
723
  ```ruby
753
- class MySafeMigration < ActiveRecord::Migration[7.1]
724
+ class MySafeMigration < ActiveRecord::Migration[8.0]
754
725
  def change
755
726
  safety_assured { remove_column :users, :some_column }
756
727
  end
@@ -761,7 +732,7 @@ Certain methods like `execute` and `change_table` cannot be inspected and are pr
761
732
 
762
733
  ## Safe by Default
763
734
 
764
- 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.
765
736
 
766
737
  - adding and removing an index
767
738
  - adding a foreign key
@@ -810,6 +781,16 @@ StrongMigrations.disable_check(:add_index)
810
781
 
811
782
  Check the [source code](https://github.com/ankane/strong_migrations/blob/master/lib/strong_migrations/error_messages.rb) for the list of keys.
812
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
+
813
794
  ## Down Migrations / Rollbacks
814
795
 
815
796
  By default, checks are disabled when migrating down. Enable them with:
@@ -891,7 +872,21 @@ production:
891
872
 
892
873
  For HTTP connections, Redis, and other services, check out [this guide](https://github.com/ankane/the-ultimate-guide-to-ruby-timeouts).
893
874
 
894
- ## 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.
895
890
 
896
891
  There’s the option to automatically retry statements for migrations when the lock timeout is reached. Here’s how it works:
897
892
 
@@ -915,7 +910,7 @@ StrongMigrations.lock_timeout_retry_delay = 10.seconds
915
910
  To mark migrations as safe that were created before installing this gem, create an initializer with:
916
911
 
917
912
  ```ruby
918
- StrongMigrations.start_after = 20230101000000
913
+ StrongMigrations.start_after = 20250101000000
919
914
  ```
920
915
 
921
916
  Use the version from your latest migration.
@@ -925,14 +920,14 @@ Use the version from your latest migration.
925
920
  If your development database version is different from production, you can specify the production version so the right checks run in development.
926
921
 
927
922
  ```ruby
928
- StrongMigrations.target_version = 10 # or "8.0.12", "10.3.2", etc
923
+ StrongMigrations.target_version = 10 # or 8.0, 10.5, etc
929
924
  ```
930
925
 
931
- The major version works well for Postgres, while the full version is recommended for MySQL and MariaDB.
926
+ The major version works well for Postgres, while the major and minor version is recommended for MySQL and MariaDB.
932
927
 
933
928
  For safety, this option only affects development and test environments. In other environments, the actual server version is always used.
934
929
 
935
- If your app has multiple databases with different versions, with Rails 6.1+, you can use:
930
+ If your app has multiple databases with different versions, you can use:
936
931
 
937
932
  ```ruby
938
933
  StrongMigrations.target_version = {primary: 13, catalog: 15}
@@ -972,15 +967,18 @@ You probably don’t need this gem for smaller projects, as operations that are
972
967
 
973
968
  ## Additional Reading
974
969
 
975
- - [Rails Migrations with No Downtime](https://pedro.herokuapp.com/past/2011/7/13/rails_migrations_with_no_downtime/)
976
970
  - [PostgreSQL at Scale: Database Schema Changes Without Downtime](https://medium.com/braintree-product-technology/postgresql-at-scale-database-schema-changes-without-downtime-20d3749ed680)
977
- - [An Overview of DDL Algorithms in MySQL](https://mydbops.wordpress.com/2020/03/04/an-overview-of-ddl-algorithms-in-mysql-covers-mysql-8/)
971
+ - [MySQL InnoDB Online DDL Operations](https://dev.mysql.com/doc/refman/en/innodb-online-ddl-operations.html)
978
972
  - [MariaDB InnoDB Online DDL Overview](https://mariadb.com/kb/en/innodb-online-ddl-overview/)
979
973
 
980
974
  ## Credits
981
975
 
982
976
  Thanks to Bob Remeika and David Waller for the [original code](https://github.com/foobarfighter/safe-migrations) and [Sean Huber](https://github.com/LendingHome/zero_downtime_migrations) for the bad/good readme format.
983
977
 
978
+ ## History
979
+
980
+ View the [changelog](https://github.com/ankane/strong_migrations/blob/master/CHANGELOG.md)
981
+
984
982
  ## Contributing
985
983
 
986
984
  Everyone is encouraged to help improve this project. Here are a few ways you can help:
@@ -21,21 +21,17 @@ module StrongMigrations
21
21
 
22
22
  def target_version
23
23
  case adapter
24
- when /mysql/
24
+ when /mysql|trilogy/
25
25
  # could try to connect to database and check for MariaDB
26
26
  # but this should be fine
27
- '"8.0.12"'
27
+ "8.0"
28
28
  else
29
29
  "10"
30
30
  end
31
31
  end
32
32
 
33
33
  def adapter
34
- if ActiveRecord::VERSION::STRING.to_f >= 6.1
35
- ActiveRecord::Base.connection_db_config.adapter.to_s
36
- else
37
- ActiveRecord::Base.connection_config[:adapter].to_s
38
- end
34
+ ActiveRecord::Base.connection_db_config.adapter.to_s
39
35
  end
40
36
 
41
37
  def postgresql?
@@ -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)
@@ -40,6 +40,9 @@ module StrongMigrations
40
40
  ["primary_key"]
41
41
  end
42
42
 
43
+ def max_constraint_name_length
44
+ end
45
+
43
46
  private
44
47
 
45
48
  def connection
@@ -55,14 +58,6 @@ module StrongMigrations
55
58
  version =
56
59
  if target_version && StrongMigrations.developer_env?
57
60
  if target_version.is_a?(Hash)
58
- # Active Record 6.0 supports multiple databases
59
- # but connection.pool.spec.name always returns "primary"
60
- # in migrations with rails db:migrate
61
- if ActiveRecord::VERSION::STRING.to_f < 6.1
62
- # error class is not shown in db:migrate output so ensure message is descriptive
63
- raise StrongMigrations::Error, "StrongMigrations.target_version does not support multiple databases for Active Record < 6.1"
64
- end
65
-
66
61
  db_config_name = connection.pool.db_config.name
67
62
  target_version.stringify_keys.fetch(db_config_name) do
68
63
  # error class is not shown in db:migrate output so ensure message is descriptive
@@ -6,7 +6,7 @@ module StrongMigrations
6
6
  end
7
7
 
8
8
  def min_version
9
- "10.2"
9
+ "10.5"
10
10
  end
11
11
 
12
12
  def server_version