strong_migrations 1.6.4 → 1.7.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: 5399a374f21a5e61350adbe125184d43664bc654cd1583804e7b01c9d6240aab
4
- data.tar.gz: 71e638db667d72b1fdf433eb27cc4f382a6472bd0f75885f5ae244990c1b768a
3
+ metadata.gz: 0b621e800497f9e9c77e0208a899f7e8d17a944caa61dffdea80911e8ddd5ead
4
+ data.tar.gz: 3f7d4275c416db6b954c1c2e1109ad5118a1c0a7cc6eb25a81203f82a4e47b35
5
5
  SHA512:
6
- metadata.gz: 4c8dbc405cb94e4b8eb92a08161cb8689996e7a6ba216b326db27a83151f5dd796d53c42e5b4dd2f8e0cb224f3f6c8f94ff81edd23063438a8143f0d36c507b9
7
- data.tar.gz: 532f43609d8006f71e325a708a74c45df8161d5957a5173701c77cc22e7dd3111562cbc70b715c6f94c955d08cda0fd43cfc577b9d902cda8a9e1fedcd4693cc
6
+ metadata.gz: '0243834c1f5b12bc637a00b49f8b7eb773dcdd5b8592a354b705d72b6e5b6a728babfb12fc3d058093086529ebfe7de34abd9f1c834bc95323b01f8a48417445'
7
+ data.tar.gz: a24deb56f0adbe0206791b9155707489d03c02f021842f9284eaadcf5a2b99c30fe6f3ae2c51fef15fc5fef9bf7066b1a72f84c446ed8c12919c43f29eee9f95
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 1.7.0 (2024-01-05)
2
+
3
+ - Added check for `add_unique_constraint`
4
+
1
5
  ## 1.6.4 (2023-10-17)
2
6
 
3
7
  - Fixed false positives with `revert`
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
- [![Build Status](https://github.com/ankane/strong_migrations/workflows/build/badge.svg?branch=master)](https://github.com/ankane/strong_migrations/actions)
11
+ [![Build Status](https://github.com/ankane/strong_migrations/actions/workflows/build.yml/badge.svg)](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
@@ -1,3 +1,3 @@
1
1
  module StrongMigrations
2
- VERSION = "1.6.4"
2
+ VERSION = "1.7.0"
3
3
  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.6.4
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: 2023-10-17 00:00:00.000000000 Z
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.4.10
78
+ rubygems_version: 3.5.3
79
79
  signing_key:
80
80
  specification_version: 4
81
81
  summary: Catch unsafe migrations in development