strong_migrations 1.6.4 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +36 -2
- data/lib/strong_migrations/checker.rb +2 -0
- data/lib/strong_migrations/checks.rb +15 -0
- data/lib/strong_migrations/error_messages.rb +20 -3
- data/lib/strong_migrations/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0b621e800497f9e9c77e0208a899f7e8d17a944caa61dffdea80911e8ddd5ead
|
4
|
+
data.tar.gz: 3f7d4275c416db6b954c1c2e1109ad5118a1c0a7cc6eb25a81203f82a4e47b35
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '0243834c1f5b12bc637a00b49f8b7eb773dcdd5b8592a354b705d72b6e5b6a728babfb12fc3d058093086529ebfe7de34abd9f1c834bc95323b01f8a48417445'
|
7
|
+
data.tar.gz: a24deb56f0adbe0206791b9155707489d03c02f021842f9284eaadcf5a2b99c30fe6f3ae2c51fef15fc5fef9bf7066b1a72f84c446ed8c12919c43f29eee9f95
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -8,7 +8,7 @@ Supports PostgreSQL, MySQL, and MariaDB
|
|
8
8
|
|
9
9
|
:tangerine: Battle-tested at [Instacart](https://www.instacart.com/opensource)
|
10
10
|
|
11
|
-
[](https://github.com/ankane/strong_migrations/actions)
|
12
12
|
|
13
13
|
## Installation
|
14
14
|
|
@@ -75,6 +75,7 @@ Postgres-specific checks:
|
|
75
75
|
- [adding an index non-concurrently](#adding-an-index-non-concurrently)
|
76
76
|
- [adding a reference](#adding-a-reference)
|
77
77
|
- [adding a foreign key](#adding-a-foreign-key)
|
78
|
+
- [adding a unique constraint](#adding-a-unique-constraint)
|
78
79
|
- [adding an exclusion constraint](#adding-an-exclusion-constraint)
|
79
80
|
- [adding a json column](#adding-a-json-column)
|
80
81
|
- [setting NOT NULL on an existing column](#setting-not-null-on-an-existing-column)
|
@@ -305,7 +306,7 @@ A safer approach is to:
|
|
305
306
|
|
306
307
|
1. Create a new table
|
307
308
|
2. Write to both tables
|
308
|
-
3. Backfill data from the old table to new table
|
309
|
+
3. Backfill data from the old table to the new table
|
309
310
|
4. Move reads from the old table to the new table
|
310
311
|
5. Stop writing to the old table
|
311
312
|
6. Drop the old table
|
@@ -512,6 +513,39 @@ class ValidateForeignKeyOnUsers < ActiveRecord::Migration[7.1]
|
|
512
513
|
end
|
513
514
|
```
|
514
515
|
|
516
|
+
### Adding a unique constraint
|
517
|
+
|
518
|
+
#### Bad
|
519
|
+
|
520
|
+
In Postgres, adding a unique constraint creates a unique index, which blocks reads and writes.
|
521
|
+
|
522
|
+
```ruby
|
523
|
+
class AddUniqueContraint < ActiveRecord::Migration[7.1]
|
524
|
+
def change
|
525
|
+
add_unique_constraint :users, :some_column
|
526
|
+
end
|
527
|
+
end
|
528
|
+
```
|
529
|
+
|
530
|
+
#### Good
|
531
|
+
|
532
|
+
Create a unique index concurrently, then use it for the constraint.
|
533
|
+
|
534
|
+
```ruby
|
535
|
+
class AddUniqueContraint < ActiveRecord::Migration[7.1]
|
536
|
+
disable_ddl_transaction!
|
537
|
+
|
538
|
+
def up
|
539
|
+
add_index :users, :some_column, unique: true, algorithm: :concurrently
|
540
|
+
add_unique_constraint :users, using_index: "index_users_on_some_column"
|
541
|
+
end
|
542
|
+
|
543
|
+
def down
|
544
|
+
remove_unique_constraint :users, :some_column
|
545
|
+
end
|
546
|
+
end
|
547
|
+
```
|
548
|
+
|
515
549
|
### Adding an exclusion constraint
|
516
550
|
|
517
551
|
#### Bad
|
@@ -48,6 +48,8 @@ module StrongMigrations
|
|
48
48
|
check_add_index(*args)
|
49
49
|
when :add_reference, :add_belongs_to
|
50
50
|
check_add_reference(method, *args)
|
51
|
+
when :add_unique_constraint
|
52
|
+
check_add_unique_constraint(*args)
|
51
53
|
when :change_column
|
52
54
|
check_change_column(*args)
|
53
55
|
when :change_column_default
|
@@ -180,6 +180,21 @@ Then add the foreign key in separate migrations."
|
|
180
180
|
end
|
181
181
|
end
|
182
182
|
|
183
|
+
def check_add_unique_constraint(*args)
|
184
|
+
args.extract_options!
|
185
|
+
table, column = args
|
186
|
+
|
187
|
+
# column and using_index cannot be used together
|
188
|
+
# check for column to ensure error message can be generated
|
189
|
+
if column && !new_table?(table)
|
190
|
+
index_name = connection.index_name(table, {column: column})
|
191
|
+
raise_error :add_unique_constraint,
|
192
|
+
index_command: command_str(:add_index, [table, column, {unique: true, algorithm: :concurrently}]),
|
193
|
+
constraint_command: command_str(:add_unique_constraint, [table, {using_index: index_name}]),
|
194
|
+
remove_command: command_str(:remove_unique_constraint, [table, column])
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
183
198
|
def check_change_column(*args)
|
184
199
|
options = args.extract_options!
|
185
200
|
table, column, type = args
|
@@ -88,7 +88,7 @@ in your application. A safer approach is to:
|
|
88
88
|
|
89
89
|
1. Create a new column
|
90
90
|
2. Write to both columns
|
91
|
-
3. Backfill data from the old column to new column
|
91
|
+
3. Backfill data from the old column to the new column
|
92
92
|
4. Move reads from the old column to the new column
|
93
93
|
5. Stop writing to the old column
|
94
94
|
6. Drop the old column",
|
@@ -99,7 +99,7 @@ in your application. A safer approach is to:
|
|
99
99
|
|
100
100
|
1. Create a new table. Don't forget to recreate indexes from the old table
|
101
101
|
2. Write to both tables
|
102
|
-
3. Backfill data from the old table to new table
|
102
|
+
3. Backfill data from the old table to the new table
|
103
103
|
4. Move reads from the old table to the new table
|
104
104
|
5. Stop writing to the old table
|
105
105
|
6. Drop the old table",
|
@@ -244,7 +244,24 @@ end",
|
|
244
244
|
Use disable_ddl_transaction! or a separate migration.",
|
245
245
|
|
246
246
|
add_exclusion_constraint:
|
247
|
-
"Adding an exclusion constraint blocks reads and writes while every row is checked."
|
247
|
+
"Adding an exclusion constraint blocks reads and writes while every row is checked.",
|
248
|
+
|
249
|
+
add_unique_constraint:
|
250
|
+
"Adding a unique constraint creates a unique index, which blocks reads and writes.
|
251
|
+
Instead, create a unique index concurrently, then use it for the constraint.
|
252
|
+
|
253
|
+
class %{migration_name} < ActiveRecord::Migration%{migration_suffix}
|
254
|
+
disable_ddl_transaction!
|
255
|
+
|
256
|
+
def up
|
257
|
+
%{index_command}
|
258
|
+
%{constraint_command}
|
259
|
+
end
|
260
|
+
|
261
|
+
def down
|
262
|
+
%{remove_command}
|
263
|
+
end
|
264
|
+
end"
|
248
265
|
}
|
249
266
|
self.enabled_checks = (error_messages.keys - [:remove_index]).map { |k| [k, {}] }.to_h
|
250
267
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: strong_migrations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2024-01-05 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
@@ -75,7 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
75
75
|
- !ruby/object:Gem::Version
|
76
76
|
version: '0'
|
77
77
|
requirements: []
|
78
|
-
rubygems_version: 3.
|
78
|
+
rubygems_version: 3.5.3
|
79
79
|
signing_key:
|
80
80
|
specification_version: 4
|
81
81
|
summary: Catch unsafe migrations in development
|