online_migrations 0.8.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9bfe87b009fa9199b4deb0a1bf7e63dac043fa8f29c25f4f93c55fd136155f2d
4
- data.tar.gz: 0a95c4b4de358769bce7d3855f7df67c08d027279b523d0ea550a383b60cd6a8
3
+ metadata.gz: 3f7396712072756a2c7de52a7057f0c46da8ce26bf59270f0f2dea42bf43d0b3
4
+ data.tar.gz: a3a26397aa2a279e996e8af2e7c698ebabc73ee38bad89b84e98cbddb609feb4
5
5
  SHA512:
6
- metadata.gz: 95b0d869484b407b295426bf1b836f00ce71c297fc1ef35fe817e449f126ccbc7e7df512efc30c16cda9dfb9755c86e1b409f9787f40708341aba422128b0d60
7
- data.tar.gz: 42b829796b7fd0503416460775ac1c7596fce43621667a9144cd06b9000e35f8054398cadbf82f370b3e8ca988bcd7ce53b505982e8e5b3ed7e6991c71f373aa
6
+ metadata.gz: d897c787f1b4e23436ecc362dd49a97eba529e7f0613fe71d38b4fde1cd740a40e0fa6ba2ef59d32f0ab6c1050ebc98728a1830d22679f98671305e1d82d6eb3
7
+ data.tar.gz: 10c2d6cc695042052c994111f71df5e19d17cf2b81a72b4ca2bce835ecced1a91dad0bfe5d2091079661c1d97dbbf066e641b80ceca196be7dd5c0e859f8ea09
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  ## master (unreleased)
2
2
 
3
+ ## 0.9.0 (2023-10-27)
4
+
5
+ - Add ability to use custom raw sql for `backfill_column_for_type_change`'s `type_cast_function`
6
+
7
+ ```ruby
8
+ backfill_column_for_type_change(:users, :company_id, type_cast_function: Arel.sql("company_id::integer"))
9
+ ```
10
+
11
+ - Fix version safety with `revert`
12
+
13
+ ## 0.8.2 (2023-09-26)
14
+
15
+ - Promote check constraint to `NOT NULL` on PostgreSQL >= 12 when changing column type
16
+ - Fix `safety_assured` with `revert`
17
+
3
18
  ## 0.8.1 (2023-08-04)
4
19
 
5
20
  - Fix `update_columns_in_batches` when multiple columns are passed
@@ -8,7 +23,7 @@
8
23
  ## 0.8.0 (2023-07-24)
9
24
 
10
25
  - Add check for `change_column_default`
11
- - Add check for `add_unique_key` (Active Record >= 7.1)
26
+ - Add check for `add_unique_constraint` (Active Record >= 7.1)
12
27
  - Add check for `add_column` with stored generated columns
13
28
 
14
29
  ## 0.7.3 (2023-05-30)
data/README.md CHANGED
@@ -55,7 +55,7 @@ An operation is classified as dangerous if it either:
55
55
  Consider the following migration:
56
56
 
57
57
  ```ruby
58
- class AddAdminToUsers < ActiveRecord::Migration[7.0]
58
+ class AddAdminToUsers < ActiveRecord::Migration[7.1]
59
59
  def change
60
60
  add_column :users, :admin, :boolean, default: false, null: false
61
61
  end
@@ -67,7 +67,7 @@ If the `users` table is large, running this migration on a live PostgreSQL < 11
67
67
  A safer approach would be to run something like the following:
68
68
 
69
69
  ```ruby
70
- class AddAdminToUsers < ActiveRecord::Migration[7.0]
70
+ class AddAdminToUsers < ActiveRecord::Migration[7.1]
71
71
  # Do not wrap the migration in a transaction so that locks are held for a shorter time.
72
72
  disable_ddl_transaction!
73
73
 
@@ -112,7 +112,7 @@ A safer approach is to:
112
112
 
113
113
  add_column_with_default takes care of all this steps:
114
114
 
115
- class AddAdminToUsers < ActiveRecord::Migration[7.0]
115
+ class AddAdminToUsers < ActiveRecord::Migration[7.1]
116
116
  disable_ddl_transaction!
117
117
 
118
118
  def change
@@ -143,7 +143,7 @@ Potentially dangerous operations:
143
143
  - [adding a reference](#adding-a-reference)
144
144
  - [adding a foreign key](#adding-a-foreign-key)
145
145
  - [adding an exclusion constraint](#adding-an-exclusion-constraint)
146
- - [adding a unique key](#adding-a-unique-key)
146
+ - [adding a unique constraint](#adding-a-unique-constraint)
147
147
  - [adding a json column](#adding-a-json-column)
148
148
  - [adding a stored generated column](#adding-a-stored-generated-column)
149
149
  - [using primary key with short integer type](#using-primary-key-with-short-integer-type)
@@ -166,7 +166,7 @@ You can also add [custom checks](#custom-checks) or [disable specific checks](#d
166
166
  Active Record caches database columns at runtime, so if you drop a column, it can cause exceptions until your app reboots.
167
167
 
168
168
  ```ruby
169
- class RemoveNameFromUsers < ActiveRecord::Migration[7.0]
169
+ class RemoveNameFromUsers < ActiveRecord::Migration[7.1]
170
170
  def change
171
171
  remove_column :users, :name
172
172
  end
@@ -195,7 +195,7 @@ end
195
195
  3. Wrap column removing in a `safety_assured` block:
196
196
 
197
197
  ```ruby
198
- class RemoveNameFromUsers < ActiveRecord::Migration[7.0]
198
+ class RemoveNameFromUsers < ActiveRecord::Migration[7.1]
199
199
  def change
200
200
  safety_assured { remove_column :users, :name }
201
201
  end
@@ -212,7 +212,7 @@ end
212
212
  In earlier versions of PostgreSQL adding a column with a non-null default value to an existing table blocks reads and writes while the entire table is rewritten.
213
213
 
214
214
  ```ruby
215
- class AddAdminToUsers < ActiveRecord::Migration[7.0]
215
+ class AddAdminToUsers < ActiveRecord::Migration[7.1]
216
216
  def change
217
217
  add_column :users, :admin, :boolean, default: false
218
218
  end
@@ -232,7 +232,7 @@ A safer approach is to:
232
232
  `add_column_with_default` helper takes care of all this steps:
233
233
 
234
234
  ```ruby
235
- class AddAdminToUsers < ActiveRecord::Migration[7.0]
235
+ class AddAdminToUsers < ActiveRecord::Migration[7.1]
236
236
  disable_ddl_transaction!
237
237
 
238
238
  def change
@@ -250,7 +250,7 @@ end
250
250
  Active Record wraps each migration in a transaction, 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/).
251
251
 
252
252
  ```ruby
253
- class AddAdminToUsers < ActiveRecord::Migration[7.0]
253
+ class AddAdminToUsers < ActiveRecord::Migration[7.1]
254
254
  def change
255
255
  add_column :users, :admin, :boolean
256
256
  User.update_all(admin: false)
@@ -265,13 +265,13 @@ Also, running a single query to update data can cause issues for large tables.
265
265
  There are three keys to backfilling safely: batching, throttling, and running it outside a transaction. Use a `update_column_in_batches` helper in a separate migration with `disable_ddl_transaction!`.
266
266
 
267
267
  ```ruby
268
- class AddAdminToUsers < ActiveRecord::Migration[7.0]
268
+ class AddAdminToUsers < ActiveRecord::Migration[7.1]
269
269
  def change
270
270
  add_column :users, :admin, :boolean
271
271
  end
272
272
  end
273
273
 
274
- class BackfillUsersAdminColumn < ActiveRecord::Migration[7.0]
274
+ class BackfillUsersAdminColumn < ActiveRecord::Migration[7.1]
275
275
  disable_ddl_transaction!
276
276
 
277
277
  def up
@@ -290,7 +290,7 @@ end
290
290
  Changing the type of an existing column blocks reads and writes while the entire table is rewritten.
291
291
 
292
292
  ```ruby
293
- class ChangeFilesSizeType < ActiveRecord::Migration[7.0]
293
+ class ChangeFilesSizeType < ActiveRecord::Migration[7.1]
294
294
  def change
295
295
  change_column :files, :size, :bigint
296
296
  end
@@ -323,7 +323,7 @@ A safer approach can be accomplished in several steps:
323
323
  1. Create a new column and keep column's data in sync:
324
324
 
325
325
  ```ruby
326
- class InitializeChangeFilesSizeType < ActiveRecord::Migration[7.0]
326
+ class InitializeChangeFilesSizeType < ActiveRecord::Migration[7.1]
327
327
  def change
328
328
  initialize_column_type_change :files, :size, :bigint
329
329
  end
@@ -336,7 +336,7 @@ which will be passed to `add_column` when creating a new column, so you can over
336
336
  2. Backfill data from the old column to the new column:
337
337
 
338
338
  ```ruby
339
- class BackfillChangeFilesSizeType < ActiveRecord::Migration[7.0]
339
+ class BackfillChangeFilesSizeType < ActiveRecord::Migration[7.1]
340
340
  disable_ddl_transaction!
341
341
 
342
342
  def up
@@ -352,7 +352,7 @@ which will be passed to `add_column` when creating a new column, so you can over
352
352
  3. Copy indexes, foreign keys, check constraints, NOT NULL constraint, swap new column in place:
353
353
 
354
354
  ```ruby
355
- class FinalizeChangeFilesSizeType < ActiveRecord::Migration[7.0]
355
+ class FinalizeChangeFilesSizeType < ActiveRecord::Migration[7.1]
356
356
  disable_ddl_transaction!
357
357
 
358
358
  def change
@@ -365,7 +365,7 @@ which will be passed to `add_column` when creating a new column, so you can over
365
365
  5. Finally, if everything is working as expected, remove copy trigger and old column:
366
366
 
367
367
  ```ruby
368
- class CleanupChangeFilesSizeType < ActiveRecord::Migration[7.0]
368
+ class CleanupChangeFilesSizeType < ActiveRecord::Migration[7.1]
369
369
  def up
370
370
  cleanup_column_type_change :files, :size
371
371
  end
@@ -385,7 +385,7 @@ which will be passed to `add_column` when creating a new column, so you can over
385
385
  Renaming a column that's in use will cause errors in your application.
386
386
 
387
387
  ```ruby
388
- class RenameUsersNameToFirstName < ActiveRecord::Migration[7.0]
388
+ class RenameUsersNameToFirstName < ActiveRecord::Migration[7.1]
389
389
  def change
390
390
  rename_column :users, :name, :first_name
391
391
  end
@@ -454,7 +454,7 @@ nor any data/indexes/foreign keys copying will be made, so will be instantaneous
454
454
  It will use a combination of a VIEW and column aliasing to work with both column names simultaneously
455
455
 
456
456
  ```ruby
457
- class InitializeRenameUsersNameToFirstName < ActiveRecord::Migration[7.0]
457
+ class InitializeRenameUsersNameToFirstName < ActiveRecord::Migration[7.1]
458
458
  def change
459
459
  initialize_column_rename :users, :name, :first_name
460
460
  end
@@ -485,7 +485,7 @@ end
485
485
  9. Remove the VIEW created in step 3 and finally rename the column:
486
486
 
487
487
  ```ruby
488
- class FinalizeRenameUsersNameToFirstName < ActiveRecord::Migration[7.0]
488
+ class FinalizeRenameUsersNameToFirstName < ActiveRecord::Migration[7.1]
489
489
  def change
490
490
  finalize_column_rename :users, :name, :first_name
491
491
  end
@@ -501,7 +501,7 @@ end
501
501
  Renaming a table that's in use will cause errors in your application.
502
502
 
503
503
  ```ruby
504
- class RenameClientsToUsers < ActiveRecord::Migration[7.0]
504
+ class RenameClientsToUsers < ActiveRecord::Migration[7.1]
505
505
  def change
506
506
  rename_table :clients, :users
507
507
  end
@@ -556,7 +556,7 @@ OnlineMigrations.config.table_renames = {
556
556
  3. Create a VIEW:
557
557
 
558
558
  ```ruby
559
- class InitializeRenameClientsToUsers < ActiveRecord::Migration[7.0]
559
+ class InitializeRenameClientsToUsers < ActiveRecord::Migration[7.1]
560
560
  def change
561
561
  initialize_table_rename :clients, :users
562
562
  end
@@ -569,7 +569,7 @@ end
569
569
  7. Remove the VIEW created in step 3:
570
570
 
571
571
  ```ruby
572
- class FinalizeRenameClientsToUsers < ActiveRecord::Migration[7.0]
572
+ class FinalizeRenameClientsToUsers < ActiveRecord::Migration[7.1]
573
573
  def change
574
574
  finalize_table_rename :clients, :users
575
575
  end
@@ -585,7 +585,7 @@ end
585
585
  The `force` option can drop an existing table.
586
586
 
587
587
  ```ruby
588
- class CreateUsers < ActiveRecord::Migration[7.0]
588
+ class CreateUsers < ActiveRecord::Migration[7.1]
589
589
  def change
590
590
  create_table :users, force: true do |t|
591
591
  # ...
@@ -599,7 +599,7 @@ end
599
599
  Create tables without the `force` option.
600
600
 
601
601
  ```ruby
602
- class CreateUsers < ActiveRecord::Migration[7.0]
602
+ class CreateUsers < ActiveRecord::Migration[7.1]
603
603
  def change
604
604
  create_table :users do |t|
605
605
  # ...
@@ -617,7 +617,7 @@ If you intend to drop an existing table, run `drop_table` first.
617
617
  Adding a check constraint blocks reads and writes while every row is checked.
618
618
 
619
619
  ```ruby
620
- class AddCheckConstraint < ActiveRecord::Migration[7.0]
620
+ class AddCheckConstraint < ActiveRecord::Migration[7.1]
621
621
  def change
622
622
  add_check_constraint :users, "char_length(name) >= 1", name: "name_check"
623
623
  end
@@ -629,7 +629,7 @@ end
629
629
  Add the check constraint without validating existing rows, and then validate them in a separate transaction:
630
630
 
631
631
  ```ruby
632
- class AddCheckConstraint < ActiveRecord::Migration[7.0]
632
+ class AddCheckConstraint < ActiveRecord::Migration[7.1]
633
633
  disable_ddl_transaction!
634
634
 
635
635
  def change
@@ -648,7 +648,7 @@ end
648
648
  Setting `NOT NULL` on an existing column blocks reads and writes while every row is checked.
649
649
 
650
650
  ```ruby
651
- class ChangeUsersNameNull < ActiveRecord::Migration[7.0]
651
+ class ChangeUsersNameNull < ActiveRecord::Migration[7.1]
652
652
  def change
653
653
  change_column_null :users, :name, false
654
654
  end
@@ -660,7 +660,7 @@ end
660
660
  Instead, add a check constraint and validate it in a separate transaction:
661
661
 
662
662
  ```ruby
663
- class ChangeUsersNameNull < ActiveRecord::Migration[7.0]
663
+ class ChangeUsersNameNull < ActiveRecord::Migration[7.1]
664
664
  disable_ddl_transaction!
665
665
 
666
666
  def change
@@ -675,7 +675,7 @@ end
675
675
  A `NOT NULL` check constraint is functionally equivalent to setting `NOT NULL` on the column (but it won't show up in `schema.rb` in Rails < 6.1). In PostgreSQL 12+, once the check constraint is validated, you can safely set `NOT NULL` on the column and drop the check constraint.
676
676
 
677
677
  ```ruby
678
- class ChangeUsersNameNullDropCheck < ActiveRecord::Migration[7.0]
678
+ class ChangeUsersNameNullDropCheck < ActiveRecord::Migration[7.1]
679
679
  def change
680
680
  # in PostgreSQL 12+, you can then safely set NOT NULL on the column
681
681
  change_column_null :users, :name, false
@@ -689,7 +689,7 @@ end
689
689
  Online Migrations does not support inspecting what happens inside an `execute` call, so cannot help you here. Make really sure that what you're doing is safe before proceeding, then wrap it in a `safety_assured { ... }` block:
690
690
 
691
691
  ```ruby
692
- class ExecuteSQL < ActiveRecord::Migration[7.0]
692
+ class ExecuteSQL < ActiveRecord::Migration[7.1]
693
693
  def change
694
694
  safety_assured { execute "..." }
695
695
  end
@@ -703,7 +703,7 @@ end
703
703
  Adding an index non-concurrently blocks writes.
704
704
 
705
705
  ```ruby
706
- class AddIndexOnUsersEmail < ActiveRecord::Migration[7.0]
706
+ class AddIndexOnUsersEmail < ActiveRecord::Migration[7.1]
707
707
  def change
708
708
  add_index :users, :email, unique: true
709
709
  end
@@ -715,7 +715,7 @@ end
715
715
  Add indexes concurrently.
716
716
 
717
717
  ```ruby
718
- class AddIndexOnUsersEmail < ActiveRecord::Migration[7.0]
718
+ class AddIndexOnUsersEmail < ActiveRecord::Migration[7.1]
719
719
  disable_ddl_transaction!
720
720
 
721
721
  def change
@@ -733,7 +733,7 @@ end
733
733
  While actual removing of an index is usually fast, removing it non-concurrently tries to obtain an `ACCESS EXCLUSIVE` lock on the table, waiting for all existing queries to complete and blocking all the subsequent queries (even `SELECT`s) on that table until the lock is obtained and index is removed.
734
734
 
735
735
  ```ruby
736
- class RemoveIndexOnUsersEmail < ActiveRecord::Migration[7.0]
736
+ class RemoveIndexOnUsersEmail < ActiveRecord::Migration[7.1]
737
737
  def change
738
738
  remove_index :users, :email
739
739
  end
@@ -745,7 +745,7 @@ end
745
745
  Remove indexes concurrently.
746
746
 
747
747
  ```ruby
748
- class RemoveIndexOnUsersEmail < ActiveRecord::Migration[7.0]
748
+ class RemoveIndexOnUsersEmail < ActiveRecord::Migration[7.1]
749
749
  disable_ddl_transaction!
750
750
 
751
751
  def change
@@ -763,7 +763,7 @@ end
763
763
  Removing an old index before replacing it with the new one might result in slow queries while building the new index.
764
764
 
765
765
  ```ruby
766
- class AddIndexOnCreationToProjects < ActiveRecord::Migration[7.0]
766
+ class AddIndexOnCreationToProjects < ActiveRecord::Migration[7.1]
767
767
  disable_ddl_transaction!
768
768
 
769
769
  def change
@@ -780,7 +780,7 @@ end
780
780
  A safer approach is to create the new index and then delete the old one.
781
781
 
782
782
  ```ruby
783
- class AddIndexOnCreationToProjects < ActiveRecord::Migration[7.0]
783
+ class AddIndexOnCreationToProjects < ActiveRecord::Migration[7.1]
784
784
  disable_ddl_transaction!
785
785
 
786
786
  def change
@@ -797,7 +797,7 @@ end
797
797
  Rails adds an index non-concurrently to references by default, which blocks writes. Additionally, if `foreign_key` option (without `validate: false`) is provided, both tables are blocked while it is validated.
798
798
 
799
799
  ```ruby
800
- class AddUserToProjects < ActiveRecord::Migration[7.0]
800
+ class AddUserToProjects < ActiveRecord::Migration[7.1]
801
801
  def change
802
802
  add_reference :projects, :user, foreign_key: true
803
803
  end
@@ -810,7 +810,7 @@ Make sure the index is added concurrently and the foreign key is added in a sepa
810
810
  Or you can use `add_reference_concurrently` helper. It will create a reference and take care of safely adding index and/or foreign key.
811
811
 
812
812
  ```ruby
813
- class AddUserToProjects < ActiveRecord::Migration[7.0]
813
+ class AddUserToProjects < ActiveRecord::Migration[7.1]
814
814
  disable_ddl_transaction!
815
815
 
816
816
  def change
@@ -828,7 +828,7 @@ end
828
828
  Adding a foreign key blocks writes on both tables.
829
829
 
830
830
  ```ruby
831
- class AddForeignKeyToProjectsUser < ActiveRecord::Migration[7.0]
831
+ class AddForeignKeyToProjectsUser < ActiveRecord::Migration[7.1]
832
832
  def change
833
833
  add_foreign_key :projects, :users
834
834
  end
@@ -838,7 +838,7 @@ end
838
838
  or
839
839
 
840
840
  ```ruby
841
- class AddReferenceToProjectsUser < ActiveRecord::Migration[7.0]
841
+ class AddReferenceToProjectsUser < ActiveRecord::Migration[7.1]
842
842
  def change
843
843
  add_reference :projects, :user, foreign_key: true
844
844
  end
@@ -850,7 +850,7 @@ end
850
850
  Add the foreign key without validating existing rows, and then validate them in a separate transaction.
851
851
 
852
852
  ```ruby
853
- class AddForeignKeyToProjectsUser < ActiveRecord::Migration[7.0]
853
+ class AddForeignKeyToProjectsUser < ActiveRecord::Migration[7.1]
854
854
  disable_ddl_transaction!
855
855
 
856
856
  def change
@@ -880,16 +880,16 @@ end
880
880
 
881
881
  [Let us know](https://github.com/fatkodima/online_migrations/issues/new) if you have a safe way to do this (exclusion constraints cannot be marked `NOT VALID`).
882
882
 
883
- ### Adding a unique key
883
+ ### Adding a unique constraint
884
884
 
885
885
  :x: **Bad**
886
886
 
887
- Adding a unique key blocks reads and writes while the underlying index is being built.
887
+ Adding a unique constraint blocks reads and writes while the underlying index is being built.
888
888
 
889
889
  ```ruby
890
- class AddUniqueKey < ActiveRecord::Migration[7.1]
890
+ class AddUniqueConstraint < ActiveRecord::Migration[7.1]
891
891
  def change
892
- add_unique_key :sections, :position, deferrable: :deferred
892
+ add_unique_constraint :sections, :position, deferrable: :deferred
893
893
  end
894
894
  end
895
895
  ```
@@ -899,7 +899,7 @@ end
899
899
  A safer approach is to create a unique index first, and then create a unique key using that index.
900
900
 
901
901
  ```ruby
902
- class AddUniqueKeyAddIndex < ActiveRecord::Migration[7.1]
902
+ class AddUniqueConstraintAddIndex < ActiveRecord::Migration[7.1]
903
903
  disable_ddl_transaction!
904
904
 
905
905
  def change
@@ -909,13 +909,13 @@ end
909
909
  ```
910
910
 
911
911
  ```ruby
912
- class AddUniqueKey < ActiveRecord::Migration[7.1]
912
+ class AddUniqueConstraint < ActiveRecord::Migration[7.1]
913
913
  def up
914
- add_unique_key :sections, :position, deferrable: :deferred, using_index: "index_sections_on_position"
914
+ add_unique_constraint :sections, :position, deferrable: :deferred, using_index: "index_sections_on_position"
915
915
  end
916
916
 
917
917
  def down
918
- remove_unique_key :sections, :position
918
+ remove_unique_constraint :sections, :position
919
919
  end
920
920
  end
921
921
  ```
@@ -927,7 +927,7 @@ end
927
927
  There's no equality operator for the `json` column type, which can cause errors for existing `SELECT DISTINCT` queries in your application.
928
928
 
929
929
  ```ruby
930
- class AddSettingsToProjects < ActiveRecord::Migration[7.0]
930
+ class AddSettingsToProjects < ActiveRecord::Migration[7.1]
931
931
  def change
932
932
  add_column :projects, :settings, :json
933
933
  end
@@ -939,7 +939,7 @@ end
939
939
  Use `jsonb` instead.
940
940
 
941
941
  ```ruby
942
- class AddSettingsToProjects < ActiveRecord::Migration[7.0]
942
+ class AddSettingsToProjects < ActiveRecord::Migration[7.1]
943
943
  def change
944
944
  add_column :projects, :settings, :jsonb
945
945
  end
@@ -953,7 +953,7 @@ end
953
953
  Adding a stored generated column causes the entire table to be rewritten. During this time, reads and writes are blocked.
954
954
 
955
955
  ```ruby
956
- class AddLowerEmailToUsers < ActiveRecord::Migration[7.0]
956
+ class AddLowerEmailToUsers < ActiveRecord::Migration[7.1]
957
957
  def change
958
958
  add_column :users, :lower_email, :virtual, type: :string, as: "LOWER(email)", stored: true
959
959
  end
@@ -971,7 +971,7 @@ Add a non-generated column and use callbacks or triggers instead.
971
971
  When using short integer types as primary key types, [there is a risk](https://m.signalvnoise.com/update-on-basecamp-3-being-stuck-in-read-only-as-of-nov-8-922am-cst/) of running out of IDs on inserts. The default type in Active Record < 5.1 for primary and foreign keys is `INTEGER`, which allows a little over of 2 billion records. Active Record 5.1 changed the default type to `BIGINT`.
972
972
 
973
973
  ```ruby
974
- class CreateUsers < ActiveRecord::Migration[7.0]
974
+ class CreateUsers < ActiveRecord::Migration[7.1]
975
975
  def change
976
976
  create_table :users, id: :integer do |t|
977
977
  # ...
@@ -985,7 +985,7 @@ end
985
985
  Use one of `bigint`, `bigserial`, `uuid` instead.
986
986
 
987
987
  ```ruby
988
- class CreateUsers < ActiveRecord::Migration[7.0]
988
+ class CreateUsers < ActiveRecord::Migration[7.1]
989
989
  def change
990
990
  create_table :users, id: :bigint do |t| # bigint is the default for Active Record >= 5.1
991
991
  # ...
@@ -1001,7 +1001,7 @@ end
1001
1001
  Hash index operations are not WAL-logged, so hash indexes might need to be rebuilt with `REINDEX` after a database crash if there were unwritten changes. Also, changes to hash indexes are not replicated over streaming or file-based replication after the initial base backup, so they give wrong answers to queries that subsequently use them. For these reasons, hash index use is discouraged.
1002
1002
 
1003
1003
  ```ruby
1004
- class AddIndexToUsersOnEmail < ActiveRecord::Migration[7.0]
1004
+ class AddIndexToUsersOnEmail < ActiveRecord::Migration[7.1]
1005
1005
  def change
1006
1006
  add_index :users, :email, unique: true, using: :hash
1007
1007
  end
@@ -1013,7 +1013,7 @@ end
1013
1013
  Use B-tree indexes instead.
1014
1014
 
1015
1015
  ```ruby
1016
- class AddIndexToUsersOnEmail < ActiveRecord::Migration[7.0]
1016
+ class AddIndexToUsersOnEmail < ActiveRecord::Migration[7.1]
1017
1017
  def change
1018
1018
  add_index :users, :email, unique: true # B-tree by default
1019
1019
  end
@@ -1028,7 +1028,7 @@ Adding multiple foreign keys in a single migration blocks reads and writes on al
1028
1028
  Avoid adding foreign key more than once per migration file, unless the source and target tables are identical.
1029
1029
 
1030
1030
  ```ruby
1031
- class CreateUserProjects < ActiveRecord::Migration[7.0]
1031
+ class CreateUserProjects < ActiveRecord::Migration[7.1]
1032
1032
  def change
1033
1033
  create_table :user_projects do |t|
1034
1034
  t.belongs_to :user, foreign_key: true
@@ -1043,7 +1043,7 @@ end
1043
1043
  Add additional foreign keys in separate migration files. See [adding a foreign key](#adding-a-foreign-key) for how to properly add foreign keys.
1044
1044
 
1045
1045
  ```ruby
1046
- class CreateUserProjects < ActiveRecord::Migration[7.0]
1046
+ class CreateUserProjects < ActiveRecord::Migration[7.1]
1047
1047
  def change
1048
1048
  create_table :user_projects do |t|
1049
1049
  t.belongs_to :user, foreign_key: true
@@ -1052,7 +1052,7 @@ class CreateUserProjects < ActiveRecord::Migration[7.0]
1052
1052
  end
1053
1053
  end
1054
1054
 
1055
- class AddForeignKeyFromUserProjectsToProject < ActiveRecord::Migration[7.0]
1055
+ class AddForeignKeyFromUserProjectsToProject < ActiveRecord::Migration[7.1]
1056
1056
  def change
1057
1057
  add_foreign_key :user_projects, :projects
1058
1058
  end
@@ -1069,7 +1069,7 @@ Remove all the foreign keys first.
1069
1069
  Assuming, `projects` has foreign keys on `users.id` and `repositories.id`:
1070
1070
 
1071
1071
  ```ruby
1072
- class DropProjects < ActiveRecord::Migration[7.0]
1072
+ class DropProjects < ActiveRecord::Migration[7.1]
1073
1073
  def change
1074
1074
  drop_table :projects
1075
1075
  end
@@ -1081,13 +1081,13 @@ end
1081
1081
  Remove all the foreign keys first:
1082
1082
 
1083
1083
  ```ruby
1084
- class RemoveProjectsUserFk < ActiveRecord::Migration[7.0]
1084
+ class RemoveProjectsUserFk < ActiveRecord::Migration[7.1]
1085
1085
  def change
1086
1086
  remove_foreign_key :projects, :users
1087
1087
  end
1088
1088
  end
1089
1089
 
1090
- class RemoveProjectsRepositoryFk < ActiveRecord::Migration[7.0]
1090
+ class RemoveProjectsRepositoryFk < ActiveRecord::Migration[7.1]
1091
1091
  def change
1092
1092
  remove_foreign_key :projects, :repositories
1093
1093
  end
@@ -1097,7 +1097,7 @@ end
1097
1097
  Then remove the table:
1098
1098
 
1099
1099
  ```ruby
1100
- class DropProjects < ActiveRecord::Migration[7.0]
1100
+ class DropProjects < ActiveRecord::Migration[7.1]
1101
1101
  def change
1102
1102
  drop_table :projects
1103
1103
  end
@@ -1114,7 +1114,7 @@ Otherwise, there's a risk of bugs caused by IDs representable by one type but no
1114
1114
  Assuming, there is a `users` table with `bigint` primary key type:
1115
1115
 
1116
1116
  ```ruby
1117
- class AddUserIdToProjects < ActiveRecord::Migration[7.0]
1117
+ class AddUserIdToProjects < ActiveRecord::Migration[7.1]
1118
1118
  def change
1119
1119
  add_column :projects, :user_id, :integer
1120
1120
  end
@@ -1128,7 +1128,7 @@ Add a reference column of the same type as a referenced primary key.
1128
1128
  Assuming, there is a `users` table with `bigint` primary key type:
1129
1129
 
1130
1130
  ```ruby
1131
- class AddUserIdToProjects < ActiveRecord::Migration[7.0]
1131
+ class AddUserIdToProjects < ActiveRecord::Migration[7.1]
1132
1132
  def change
1133
1133
  add_column :projects, :user_id, :bigint
1134
1134
  end
@@ -1142,7 +1142,7 @@ end
1142
1142
  Adding a single table inheritance column might cause errors in old instances of your application.
1143
1143
 
1144
1144
  ```ruby
1145
- class AddTypeToUsers < ActiveRecord::Migration[7.0]
1145
+ class AddTypeToUsers < ActiveRecord::Migration[7.1]
1146
1146
  def change
1147
1147
  add_column :users, :string, :type, default: "Member"
1148
1148
  end
@@ -1182,7 +1182,7 @@ A safer approach is to:
1182
1182
  Active Record < 7 enables partial writes by default, which can cause incorrect values to be inserted when changing the default value of a column.
1183
1183
 
1184
1184
  ```ruby
1185
- class ChangeSomeColumnDefault < ActiveRecord::Migration[7.0]
1185
+ class ChangeSomeColumnDefault < ActiveRecord::Migration[7.1]
1186
1186
  def change
1187
1187
  change_column_default :users, :some_column, from: "old", to: "new"
1188
1188
  end
@@ -1210,7 +1210,7 @@ config.active_record.partial_inserts = false
1210
1210
  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.
1211
1211
 
1212
1212
  ```ruby
1213
- class MySafeMigration < ActiveRecord::Migration[7.0]
1213
+ class MySafeMigration < ActiveRecord::Migration[7.1]
1214
1214
  def change
1215
1215
  safety_assured { remove_column :users, :some_column }
1216
1216
  end
data/docs/configuring.md CHANGED
@@ -154,7 +154,7 @@ This is useful to demystify `online_migrations` inner workings, and to better in
154
154
  Consider migration, running on PostgreSQL < 11:
155
155
 
156
156
  ```ruby
157
- class AddAdminToUsers < ActiveRecord::Migration[7.0]
157
+ class AddAdminToUsers < ActiveRecord::Migration[7.1]
158
158
  disable_ddl_transaction!
159
159
 
160
160
  def change
@@ -21,8 +21,8 @@ module OnlineMigrations
21
21
  migration = "#{namespace}::#{name}".safe_constantize ||
22
22
  "#{internal_namespace}::#{name}".safe_constantize
23
23
 
24
- raise NotFoundError.new("Background Migration #{name} not found", name) unless migration
25
- unless migration.is_a?(Class) && migration < self
24
+ raise NotFoundError.new("Background Migration #{name} not found", name) if migration.nil?
25
+ if !(migration.is_a?(Class) && migration < self)
26
26
  raise NotFoundError.new("#{name} is not a Background Migration", name)
27
27
  end
28
28
 
@@ -8,7 +8,7 @@ module OnlineMigrations
8
8
  relation = record.migration_relation
9
9
  migration_name = record.migration_name
10
10
 
11
- unless relation.is_a?(ActiveRecord::Relation)
11
+ if !relation.is_a?(ActiveRecord::Relation)
12
12
  record.errors.add(
13
13
  :migration_name,
14
14
  "#{migration_name}#relation must return an ActiveRecord::Relation object"
@@ -87,7 +87,7 @@ module OnlineMigrations
87
87
  end
88
88
 
89
89
  def throttler=(value)
90
- unless value.respond_to?(:call)
90
+ if !value.respond_to?(:call)
91
91
  raise ArgumentError, "background_migrations throttler must be a callable."
92
92
  end
93
93
 
@@ -13,7 +13,7 @@ module OnlineMigrations
13
13
  end
14
14
 
15
15
  def relation
16
- unless @record.respond_to?(association)
16
+ if !@record.respond_to?(association)
17
17
  raise ArgumentError, "'#{@record.class.name}' has no association called '#{association}'"
18
18
  end
19
19
 
@@ -16,7 +16,7 @@ module OnlineMigrations
16
16
  # https://github.com/rails/rails/pull/34727
17
17
  associations.inject(model.unscoped) do |relation, association|
18
18
  reflection = model.reflect_on_association(association)
19
- unless reflection
19
+ if reflection.nil?
20
20
  raise ArgumentError, "'#{model.name}' has no association called '#{association}'"
21
21
  end
22
22
 
@@ -11,12 +11,12 @@ module OnlineMigrations
11
11
  }
12
12
 
13
13
  def validate(record)
14
- return unless record.status_changed?
14
+ return if !record.status_changed?
15
15
 
16
16
  previous_status, new_status = record.status_change
17
17
  valid_new_statuses = VALID_STATUS_TRANSITIONS.fetch(previous_status, [])
18
18
 
19
- unless valid_new_statuses.include?(new_status)
19
+ if !valid_new_statuses.include?(new_status)
20
20
  record.errors.add(
21
21
  :status,
22
22
  "cannot transition background migration job from status #{previous_status} to #{new_status}"
@@ -29,12 +29,12 @@ module OnlineMigrations
29
29
  }
30
30
 
31
31
  def validate(record)
32
- return unless record.status_changed?
32
+ return if !record.status_changed?
33
33
 
34
34
  previous_status, new_status = record.status_change
35
35
  valid_new_statuses = VALID_STATUS_TRANSITIONS.fetch(previous_status, [])
36
36
 
37
- unless valid_new_statuses.include?(new_status)
37
+ if !valid_new_statuses.include?(new_status)
38
38
  record.errors.add(
39
39
  :status,
40
40
  "cannot transition background migration from status #{previous_status} to #{new_status}"
@@ -59,7 +59,7 @@ module OnlineMigrations
59
59
  def has_many_association(counter_association) # rubocop:disable Naming/PredicateName
60
60
  has_many_association = model.reflect_on_association(counter_association)
61
61
 
62
- unless has_many_association
62
+ if !has_many_association
63
63
  has_many = model.reflect_on_all_associations(:has_many)
64
64
 
65
65
  has_many_association = has_many.find do |association|
@@ -74,7 +74,7 @@ module OnlineMigrations
74
74
 
75
75
  counter_association = has_many_association.plural_name if has_many_association
76
76
  end
77
- raise ArgumentError, "'#{model.name}' has no association called '#{counter_association}'" unless has_many_association
77
+ raise ArgumentError, "'#{model.name}' has no association called '#{counter_association}'" if !has_many_association
78
78
 
79
79
  if has_many_association.is_a?(ActiveRecord::Reflection::ThroughReflection)
80
80
  has_many_association = has_many_association.through_reflection
@@ -14,7 +14,7 @@ module OnlineMigrations
14
14
  end
15
15
 
16
16
  def each_batch(of: 1000, column: relation.primary_key, start: nil, finish: nil, order: :asc)
17
- unless [:asc, :desc].include?(order)
17
+ if ![:asc, :desc].include?(order)
18
18
  raise ArgumentError, ":order must be :asc or :desc, got #{order.inspect}"
19
19
  end
20
20
 
@@ -26,7 +26,7 @@ module OnlineMigrations
26
26
 
27
27
  start_row = base_relation.uncached { base_relation.first }
28
28
 
29
- return unless start_row
29
+ return if !start_row
30
30
 
31
31
  start_id = start_row[column]
32
32
  arel_table = relation.arel_table
@@ -67,7 +67,7 @@ module OnlineMigrations
67
67
  # Retaining the results in the query cache would undermine the point of batching.
68
68
  batch_relation.uncached { yield batch_relation, index }
69
69
 
70
- break unless stop_row
70
+ break if !stop_row
71
71
  end
72
72
  end
73
73
 
@@ -113,21 +113,22 @@ module OnlineMigrations
113
113
 
114
114
  if raw_connection.server_version >= 11_00_00
115
115
  if primary_key(table_name) == column_name.to_s && old_col.type == :integer
116
- # If the column to be converted is a Primary Key, set it to
117
- # `NOT NULL DEFAULT 0` and we'll copy the correct values when backfilling.
118
- # That way, we skip the expensive validation step required to add
119
- # a `NOT NULL` constraint at the end of the process.
116
+ # For PG < 11 and Primary Key conversions, setting a column as the PK
117
+ # converts even check constraints to NOT NULL column constraints
118
+ # and forces an inline re-verification of the whole table.
119
+ # To avoid this, we instead set it to `NOT NULL DEFAULT 0` and we'll
120
+ # copy the correct values when backfilling.
120
121
  add_column(table_name, tmp_column_name, new_type,
121
122
  **old_col_options.merge(column_options).merge(default: old_col.default || 0, null: false))
122
123
  else
123
- unless old_col.default.nil?
124
+ if !old_col.default.nil?
124
125
  old_col_options = old_col_options.merge(default: old_col.default, null: old_col.null)
125
126
  end
126
127
  add_column(table_name, tmp_column_name, new_type, **old_col_options.merge(column_options))
127
128
  end
128
129
  else
129
130
  add_column(table_name, tmp_column_name, new_type, **old_col_options.merge(column_options))
130
- change_column_default(table_name, tmp_column_name, old_col.default) unless old_col.default.nil?
131
+ change_column_default(table_name, tmp_column_name, old_col.default) if !old_col.default.nil?
131
132
  end
132
133
  end
133
134
 
@@ -175,6 +176,7 @@ module OnlineMigrations
175
176
  #
176
177
  # @example With type casting
177
178
  # backfill_column_for_type_change(:users, :settings, type_cast_function: "jsonb")
179
+ # backfill_column_for_type_change(:users, :company_id, type_cast_function: Arel.sql("company_id::integer"))
178
180
  #
179
181
  # @example Additional batch options
180
182
  # backfill_column_for_type_change(:files, :size, batch_size: 10_000)
@@ -201,7 +203,13 @@ module OnlineMigrations
201
203
 
202
204
  old_value = Arel::Table.new(table_name)[column_name]
203
205
  if (type_cast_function = type_cast_functions.with_indifferent_access[column_name])
204
- old_value = Arel::Nodes::NamedFunction.new(type_cast_function.to_s, [old_value])
206
+ old_value =
207
+ case type_cast_function
208
+ when Arel::Nodes::SqlLiteral
209
+ type_cast_function
210
+ else
211
+ Arel::Nodes::NamedFunction.new(type_cast_function.to_s, [old_value])
212
+ end
205
213
  end
206
214
 
207
215
  [tmp_column, old_value]
@@ -247,22 +255,7 @@ module OnlineMigrations
247
255
 
248
256
  # At this point we are sure there are no NULLs in this column
249
257
  transaction do
250
- # For PG < 11 and Primary Key conversions, setting a column as the PK
251
- # converts even check constraints to NOT NULL column constraints
252
- # and forces an inline re-verification of the whole table.
253
- #
254
- # For PG >= 12 we can "promote" CHECK constraint to NOT NULL constraint,
255
- # but for older versions we can set attribute as NOT NULL directly
256
- # through PG internal tables.
257
- # In-depth analysis of implications of this was made, so this approach
258
- # is considered safe - https://habr.com/ru/company/haulmont/blog/493954/ (in russian).
259
- execute(<<-SQL.strip_heredoc)
260
- UPDATE pg_catalog.pg_attribute
261
- SET attnotnull = true
262
- WHERE attrelid = #{quote(table_name)}::regclass
263
- AND attname = #{quote(tmp_column_name)}
264
- SQL
265
-
258
+ __set_not_null(table_name, tmp_column_name)
266
259
  remove_not_null_constraint(table_name, tmp_column_name)
267
260
  end
268
261
  end
@@ -389,7 +382,7 @@ module OnlineMigrations
389
382
  options.each do |option|
390
383
  if column.respond_to?(option)
391
384
  value = column.public_send(option)
392
- result[option] = value unless value.nil?
385
+ result[option] = value if !value.nil?
393
386
  end
394
387
  end
395
388
  result
@@ -417,7 +410,7 @@ module OnlineMigrations
417
410
  end
418
411
 
419
412
  # This is necessary as we can't properly rename indexes such as "taggings_idx".
420
- unless index.name.include?(from_column)
413
+ if !index.name.include?(from_column)
421
414
  raise "The index #{index.name} can not be copied as it does not " \
422
415
  "mention the old column. You have to rename this index manually first."
423
416
  end
@@ -482,6 +475,29 @@ module OnlineMigrations
482
475
  end
483
476
  end
484
477
 
478
+ def __set_not_null(table_name, column_name)
479
+ # For PG >= 12 we can "promote" CHECK constraint to NOT NULL constraint:
480
+ # https://github.com/postgres/postgres/commit/bbb96c3704c041d139181c6601e5bc770e045d26
481
+ if raw_connection.server_version >= 12_00_00
482
+ execute(<<-SQL.strip_heredoc)
483
+ ALTER TABLE #{quote_table_name(table_name)}
484
+ ALTER #{quote_column_name(column_name)}
485
+ SET NOT NULL
486
+ SQL
487
+ else
488
+ # For older versions we can set attribute as NOT NULL directly
489
+ # through PG internal tables.
490
+ # In-depth analysis of implications of this was made, so this approach
491
+ # is considered safe - https://habr.com/ru/company/haulmont/blog/493954/ (in russian).
492
+ execute(<<-SQL.strip_heredoc)
493
+ UPDATE pg_catalog.pg_attribute
494
+ SET attnotnull = true
495
+ WHERE attrelid = #{quote(table_name)}::regclass
496
+ AND attname = #{quote(column_name)}
497
+ SQL
498
+ end
499
+ end
500
+
485
501
  def __check_constraints_for(table_name, column_name)
486
502
  __check_constraints(table_name).select { |c| c["column_name"] == column_name }
487
503
  end
@@ -7,11 +7,22 @@ require "set"
7
7
  module OnlineMigrations
8
8
  # @private
9
9
  class CommandChecker
10
+ class << self
11
+ attr_accessor :safe
12
+
13
+ def safety_assured
14
+ prev_value = safe
15
+ self.safe = true
16
+ yield
17
+ ensure
18
+ self.safe = prev_value
19
+ end
20
+ end
21
+
10
22
  attr_accessor :direction
11
23
 
12
24
  def initialize(migration)
13
25
  @migration = migration
14
- @safe = false
15
26
  @new_tables = []
16
27
  @new_columns = []
17
28
  @lock_timeout_checked = false
@@ -19,19 +30,11 @@ module OnlineMigrations
19
30
  @removed_indexes = []
20
31
  end
21
32
 
22
- def safety_assured
23
- prev_value = @safe
24
- @safe = true
25
- yield
26
- ensure
27
- @safe = prev_value
28
- end
29
-
30
33
  def check(command, *args, &block)
31
34
  check_database_version
32
35
  check_lock_timeout
33
36
 
34
- unless safe?
37
+ if !safe?
35
38
  do_check(command, *args, &block)
36
39
 
37
40
  run_custom_checks(command, args)
@@ -45,6 +48,10 @@ module OnlineMigrations
45
48
  end
46
49
  ruby2_keywords(:check) if respond_to?(:ruby2_keywords, true)
47
50
 
51
+ def version_safe?
52
+ version && version <= OnlineMigrations.config.start_after
53
+ end
54
+
48
55
  private
49
56
  def check_database_version
50
57
  return if defined?(@database_version_checked)
@@ -101,10 +108,11 @@ module OnlineMigrations
101
108
  end
102
109
 
103
110
  def safe?
104
- @safe ||
111
+ self.class.safe ||
105
112
  ENV["SAFETY_ASSURED"] ||
106
113
  (direction == :down && !OnlineMigrations.config.check_down) ||
107
- version <= OnlineMigrations.config.start_after
114
+ version_safe? ||
115
+ @migration.reverting?
108
116
  end
109
117
 
110
118
  def version
@@ -477,7 +485,7 @@ module OnlineMigrations
477
485
  bad_foreign_key: bad_foreign_key
478
486
  end
479
487
 
480
- unless options[:polymorphic]
488
+ if !options[:polymorphic]
481
489
  type = (options[:type] || (Utils.ar_version >= 5.1 ? :bigint : :integer)).to_sym
482
490
  column_name = "#{ref_name}_id"
483
491
 
@@ -507,9 +515,9 @@ module OnlineMigrations
507
515
  existing_indexes = connection.indexes(table_name)
508
516
 
509
517
  @removed_indexes.each do |removed_index|
510
- next unless removed_index.intersect?(index)
518
+ next if !removed_index.intersect?(index)
511
519
 
512
- unless existing_indexes.any? { |existing_index| removed_index.covered_by?(existing_index) }
520
+ if existing_indexes.none? { |existing_index| removed_index.covered_by?(existing_index) }
513
521
  raise_error :replace_index
514
522
  end
515
523
  end
@@ -560,7 +568,7 @@ module OnlineMigrations
560
568
  end
561
569
 
562
570
  def add_exclusion_constraint(table_name, _expression, **_options)
563
- unless new_or_small_table?(table_name)
571
+ if !new_or_small_table?(table_name)
564
572
  raise_error :add_exclusion_constraint
565
573
  end
566
574
  end
@@ -575,15 +583,15 @@ module OnlineMigrations
575
583
  end
576
584
  end
577
585
 
578
- def add_unique_key(table_name, column_name = nil, **options)
586
+ def add_unique_constraint(table_name, column_name = nil, **options)
579
587
  return if new_or_small_table?(table_name) || options[:using_index] || !column_name
580
588
 
581
589
  index_name = index_name(table_name, column_name)
582
590
 
583
- raise_error :add_unique_key,
591
+ raise_error :add_unique_constraint,
584
592
  add_index_code: command_str(:add_index, table_name, column_name, unique: true, name: index_name, algorithm: :concurrently),
585
- add_code: command_str(:add_unique_key, table_name, **options.merge(using_index: index_name)),
586
- remove_code: command_str(:remove_unique_key, table_name, column_name)
593
+ add_code: command_str(:add_unique_constraint, table_name, **options.merge(using_index: index_name)),
594
+ remove_code: command_str(:remove_unique_constraint, table_name, column_name)
587
595
  end
588
596
 
589
597
  # Implementation is from Active Record
@@ -98,7 +98,7 @@ module OnlineMigrations
98
98
 
99
99
  def invert_revert_initialize_columns_rename(args)
100
100
  _table, old_new_column_hash = args
101
- unless old_new_column_hash
101
+ if !old_new_column_hash
102
102
  raise ActiveRecord::IrreversibleMigration,
103
103
  "revert_initialize_columns_rename is only reversible if given a hash of old and new columns."
104
104
  end
@@ -107,7 +107,7 @@ module OnlineMigrations
107
107
 
108
108
  def invert_finalize_table_rename(args)
109
109
  _table_name, new_name = args
110
- unless new_name
110
+ if !new_name
111
111
  raise ActiveRecord::IrreversibleMigration,
112
112
  "finalize_table_rename is only reversible if given a new_name."
113
113
  end
@@ -115,7 +115,7 @@ module OnlineMigrations
115
115
  end
116
116
 
117
117
  def invert_revert_initialize_column_type_change(args)
118
- unless args[2]
118
+ if !args[2]
119
119
  raise ActiveRecord::IrreversibleMigration,
120
120
  "revert_initialize_column_type_change is only reversible if given a new_type."
121
121
  end
@@ -141,7 +141,7 @@ module OnlineMigrations
141
141
  end
142
142
 
143
143
  def invert_remove_text_limit_constraint(args)
144
- unless args[2]
144
+ if !args[2]
145
145
  raise ActiveRecord::IrreversibleMigration, "remove_text_limit_constraint is only reversible if given a limit."
146
146
  end
147
147
 
@@ -261,7 +261,7 @@ module OnlineMigrations
261
261
 
262
262
  private
263
263
  def ensure_supports_multiple_dbs
264
- unless Utils.supports_multiple_dbs?
264
+ if !Utils.supports_multiple_dbs?
265
265
  raise "OnlineMigrations does not support multiple databases for Active Record < 6.1"
266
266
  end
267
267
  end
@@ -438,9 +438,9 @@ class <%= migration_name %> < <%= migration_parent %>
438
438
  end
439
439
  end",
440
440
 
441
- add_unique_key:
442
- "Adding a unique key blocks reads and writes while the underlying index is being built.
443
- A safer approach is to create a unique index first, and then create a unique key using that index.
441
+ add_unique_constraint:
442
+ "Adding a unique constraint blocks reads and writes while the underlying index is being built.
443
+ A safer approach is to create a unique index first, and then create a unique constraint using that index.
444
444
 
445
445
  class <%= migration_name %>AddIndex < <%= migration_parent %>
446
446
  disable_ddl_transaction!
@@ -30,13 +30,22 @@ module OnlineMigrations
30
30
  end
31
31
  ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
32
32
 
33
+ # @private
34
+ def revert(*args)
35
+ if command_checker.version_safe?
36
+ safety_assured { super }
37
+ else
38
+ super
39
+ end
40
+ end
41
+
33
42
  # Mark a command in the migration as safe, despite using a method that might otherwise be dangerous.
34
43
  #
35
44
  # @example
36
45
  # safety_assured { remove_column(:users, :some_column) }
37
46
  #
38
47
  def safety_assured(&block)
39
- command_checker.safety_assured(&block)
48
+ command_checker.class.safety_assured(&block)
40
49
  end
41
50
 
42
51
  # Stop running migrations.
@@ -87,7 +87,7 @@ module OnlineMigrations
87
87
  value = Arel.sql(value.call.to_s) if value.is_a?(Proc)
88
88
 
89
89
  # Ignore subqueries in conditions
90
- unless value.is_a?(Arel::Nodes::SqlLiteral) && value.to_s =~ /select\s+/i
90
+ if !value.is_a?(Arel::Nodes::SqlLiteral) || value.to_s !~ /select\s+/i
91
91
  arel_column = model.arel_table[column_name]
92
92
  if value.nil?
93
93
  arel_column.not_eq(nil)
@@ -665,7 +665,7 @@ module OnlineMigrations
665
665
  __ensure_not_in_transaction!
666
666
 
667
667
  column_name = "#{ref_name}_id"
668
- unless column_exists?(table_name, column_name)
668
+ if !column_exists?(table_name, column_name)
669
669
  type = options[:type] || (Utils.ar_version >= 5.1 ? :bigint : :integer)
670
670
  allow_null = options.fetch(:null, true)
671
671
  add_column(table_name, column_name, type, null: allow_null)
@@ -139,7 +139,7 @@ module OnlineMigrations
139
139
  private_constant :FUNCTION_CALL_RE
140
140
 
141
141
  def volatile_default?(connection, type, value)
142
- return false unless value.is_a?(Proc) || (type.to_s == "uuid" && value.is_a?(String))
142
+ return false if !(value.is_a?(Proc) || (type.to_s == "uuid" && value.is_a?(String)))
143
143
 
144
144
  value = value.call if value.is_a?(Proc)
145
145
  return false if !value.is_a?(String)
@@ -13,7 +13,13 @@ module OnlineMigrations
13
13
  stdout_logger.level = @activerecord_logger_was.level
14
14
  stdout_logger = ActiveSupport::TaggedLogging.new(stdout_logger)
15
15
 
16
- combined_logger = stdout_logger.extend(ActiveSupport::Logger.broadcast(@activerecord_logger_was))
16
+ combined_logger =
17
+ # Broadcasting logs API was changed in https://github.com/rails/rails/pull/48615.
18
+ if Utils.ar_version >= 7.1
19
+ ActiveSupport::BroadcastLogger.new(stdout_logger, @activerecord_logger_was)
20
+ else
21
+ stdout_logger.extend(ActiveSupport::Logger.broadcast(@activerecord_logger_was))
22
+ end
17
23
 
18
24
  ActiveRecord::Base.logger = combined_logger
19
25
  set_verbose_query_logs(false)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OnlineMigrations
4
- VERSION = "0.8.1"
4
+ VERSION = "0.9.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: online_migrations
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - fatkodima
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-08-04 00:00:00.000000000 Z
11
+ date: 2023-10-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -102,7 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
102
  - !ruby/object:Gem::Version
103
103
  version: '0'
104
104
  requirements: []
105
- rubygems_version: 3.4.6
105
+ rubygems_version: 3.4.10
106
106
  signing_key:
107
107
  specification_version: 4
108
108
  summary: Catch unsafe PostgreSQL migrations in development and run them easier in