online_migrations 0.8.2 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +59 -59
- data/docs/configuring.md +1 -1
- data/lib/online_migrations/change_column_type_helpers.rb +17 -4
- data/lib/online_migrations/command_checker.rb +6 -1
- data/lib/online_migrations/migration.rb +9 -0
- data/lib/online_migrations/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3dcce4daf35232504ad0f681e81c147d71bc9a1e34004cd387c50a89f6ef66df
|
4
|
+
data.tar.gz: 0ae067917657f91f5956589cb1720faf6232fff7259b906ff69dd8bf13c35ecb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9652ccc8a70af95729cfe33fd039ad5c5120a6d497b8b5334463dbc6ea5f5d4440f9cfbd948f40127f7f3a2a89ec5b2ad4a0032c85907fadb368192127aba6f1
|
7
|
+
data.tar.gz: a5da561d0357d0f58aeaf29bd83688cd08c67e9f85dae7b7c806e347c49c2bc3a0c8410f25449cc37d952be7079be030ec591df8a9a6e072e5a1acd82d7a2a7c
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
## master (unreleased)
|
2
2
|
|
3
|
+
## 0.9.1 (2023-10-30)
|
4
|
+
|
5
|
+
- Fix copying expression indexes when changing column type
|
6
|
+
|
7
|
+
## 0.9.0 (2023-10-27)
|
8
|
+
|
9
|
+
- Add ability to use custom raw sql for `backfill_column_for_type_change`'s `type_cast_function`
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
backfill_column_for_type_change(:users, :company_id, type_cast_function: Arel.sql("company_id::integer"))
|
13
|
+
```
|
14
|
+
|
15
|
+
- Fix version safety with `revert`
|
16
|
+
|
3
17
|
## 0.8.2 (2023-09-26)
|
4
18
|
|
5
19
|
- Promote check constraint to `NOT NULL` on PostgreSQL >= 12 when changing column type
|
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.
|
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.
|
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.
|
115
|
+
class AddAdminToUsers < ActiveRecord::Migration[7.1]
|
116
116
|
disable_ddl_transaction!
|
117
117
|
|
118
118
|
def change
|
@@ -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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
853
|
+
class AddForeignKeyToProjectsUser < ActiveRecord::Migration[7.1]
|
854
854
|
disable_ddl_transaction!
|
855
855
|
|
856
856
|
def change
|
@@ -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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
157
|
+
class AddAdminToUsers < ActiveRecord::Migration[7.1]
|
158
158
|
disable_ddl_transaction!
|
159
159
|
|
160
160
|
def change
|
@@ -176,6 +176,7 @@ module OnlineMigrations
|
|
176
176
|
#
|
177
177
|
# @example With type casting
|
178
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"))
|
179
180
|
#
|
180
181
|
# @example Additional batch options
|
181
182
|
# backfill_column_for_type_change(:files, :size, batch_size: 10_000)
|
@@ -202,7 +203,13 @@ module OnlineMigrations
|
|
202
203
|
|
203
204
|
old_value = Arel::Table.new(table_name)[column_name]
|
204
205
|
if (type_cast_function = type_cast_functions.with_indifferent_access[column_name])
|
205
|
-
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
|
206
213
|
end
|
207
214
|
|
208
215
|
[tmp_column, old_value]
|
@@ -398,9 +405,15 @@ module OnlineMigrations
|
|
398
405
|
to_column = to_column.to_s
|
399
406
|
|
400
407
|
__indexes_for(table_name, from_column).each do |index|
|
401
|
-
new_columns =
|
402
|
-
|
403
|
-
|
408
|
+
new_columns =
|
409
|
+
# Expression index.
|
410
|
+
if index.columns.is_a?(String)
|
411
|
+
index.columns.gsub(from_column, to_column)
|
412
|
+
else
|
413
|
+
index.columns.map do |column|
|
414
|
+
column == from_column ? to_column : column
|
415
|
+
end
|
416
|
+
end
|
404
417
|
|
405
418
|
# This is necessary as we can't properly rename indexes such as "taggings_idx".
|
406
419
|
if !index.name.include?(from_column)
|
@@ -48,6 +48,10 @@ module OnlineMigrations
|
|
48
48
|
end
|
49
49
|
ruby2_keywords(:check) if respond_to?(:ruby2_keywords, true)
|
50
50
|
|
51
|
+
def version_safe?
|
52
|
+
version && version <= OnlineMigrations.config.start_after
|
53
|
+
end
|
54
|
+
|
51
55
|
private
|
52
56
|
def check_database_version
|
53
57
|
return if defined?(@database_version_checked)
|
@@ -107,7 +111,8 @@ module OnlineMigrations
|
|
107
111
|
self.class.safe ||
|
108
112
|
ENV["SAFETY_ASSURED"] ||
|
109
113
|
(direction == :down && !OnlineMigrations.config.check_down) ||
|
110
|
-
|
114
|
+
version_safe? ||
|
115
|
+
@migration.reverting?
|
111
116
|
end
|
112
117
|
|
113
118
|
def version
|
@@ -30,6 +30,15 @@ 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
|
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.
|
4
|
+
version: 0.9.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- fatkodima
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-10-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|