strong_migrations 1.4.4 → 1.6.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 +9 -0
- data/README.md +53 -0
- data/lib/strong_migrations/checker.rb +3 -0
- data/lib/strong_migrations/checks.rb +24 -1
- data/lib/strong_migrations/error_messages.rb +10 -0
- 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: 23737504a1d3beb08bf2412b161b8e59a8a620b4f4d993f11fe931e7f1772452
|
4
|
+
data.tar.gz: 993dc5f9d78cba148540a22772c18ed115ea6ae8075dd209cc3a869cc2a647a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e906dc1c79d12a72a7d0e6fb4ca87319095d3b0a39f2da1dbd08b453d19e3215892745c86f80bf9ea26bbf07173d87022aab21b424a881f24744b65396fcd9bc
|
7
|
+
data.tar.gz: 56a0dadcb236f09a1a7d5450456ae343ac8cb6662b65f983c28b4268edad6e7cbecfa47c86ebb0d1da72dcc1b305e7196521811a48838c25beb1119846a2b928
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
## 1.6.0 (2023-07-22)
|
2
|
+
|
3
|
+
- Added check for `change_column_default`
|
4
|
+
|
5
|
+
## 1.5.0 (2023-07-02)
|
6
|
+
|
7
|
+
- Added check for `add_column` with stored generated columns
|
8
|
+
- Fixed `add_reference` with `foreign_key` and `index: false`
|
9
|
+
|
1
10
|
## 1.4.4 (2023-03-08)
|
2
11
|
|
3
12
|
- Fixed `add_foreign_key` with `name` and `column` options with `safe_by_default`
|
data/README.md
CHANGED
@@ -62,6 +62,7 @@ Potentially dangerous operations:
|
|
62
62
|
- [removing a column](#removing-a-column)
|
63
63
|
- [adding a column with a default value](#adding-a-column-with-a-default-value)
|
64
64
|
- [backfilling data](#backfilling-data)
|
65
|
+
- [adding a stored generated column](#adding-a-stored-generated-column)
|
65
66
|
- [changing the type of a column](#changing-the-type-of-a-column)
|
66
67
|
- [renaming a column](#renaming-a-column)
|
67
68
|
- [renaming a table](#renaming-a-table)
|
@@ -78,6 +79,10 @@ Postgres-specific checks:
|
|
78
79
|
- [adding a json column](#adding-a-json-column)
|
79
80
|
- [setting NOT NULL on an existing column](#setting-not-null-on-an-existing-column)
|
80
81
|
|
82
|
+
Config-specific checks:
|
83
|
+
|
84
|
+
- [changing the default value of a column](#changing-the-default-value-of-a-column)
|
85
|
+
|
81
86
|
Best practices:
|
82
87
|
|
83
88
|
- [keeping non-unique indexes to three columns or less](#keeping-non-unique-indexes-to-three-columns-or-less)
|
@@ -191,6 +196,24 @@ class BackfillSomeColumn < ActiveRecord::Migration[7.0]
|
|
191
196
|
end
|
192
197
|
```
|
193
198
|
|
199
|
+
### Adding a stored generated column
|
200
|
+
|
201
|
+
#### Bad
|
202
|
+
|
203
|
+
Adding a stored generated column causes the entire table to be rewritten. During this time, reads and writes are blocked in Postgres, and writes are blocked in MySQL and MariaDB.
|
204
|
+
|
205
|
+
```ruby
|
206
|
+
class AddSomeColumnToUsers < ActiveRecord::Migration[7.0]
|
207
|
+
def change
|
208
|
+
add_column :users, :some_column, :virtual, type: :string, as: "...", stored: true
|
209
|
+
end
|
210
|
+
end
|
211
|
+
```
|
212
|
+
|
213
|
+
#### Good
|
214
|
+
|
215
|
+
Add a non-generated column and use callbacks or triggers instead (or a virtual generated column with MySQL and MariaDB).
|
216
|
+
|
194
217
|
### Changing the type of a column
|
195
218
|
|
196
219
|
#### Bad
|
@@ -609,6 +632,36 @@ class ValidateSomeColumnNotNull < ActiveRecord::Migration[6.0]
|
|
609
632
|
end
|
610
633
|
```
|
611
634
|
|
635
|
+
### Changing the default value of a column
|
636
|
+
|
637
|
+
#### Bad
|
638
|
+
|
639
|
+
Rails < 7 enables partial writes by default, which can cause incorrect values to be inserted when changing the default value of a column.
|
640
|
+
|
641
|
+
```ruby
|
642
|
+
class ChangeSomeColumnDefault < ActiveRecord::Migration[6.1]
|
643
|
+
def change
|
644
|
+
change_column_default :users, :some_column, from: "old", to: "new"
|
645
|
+
end
|
646
|
+
end
|
647
|
+
|
648
|
+
User.create!(some_column: "old") # can insert "new"
|
649
|
+
```
|
650
|
+
|
651
|
+
#### Good
|
652
|
+
|
653
|
+
Disable partial writes in `config/application.rb`. For Rails < 7, use:
|
654
|
+
|
655
|
+
```ruby
|
656
|
+
config.active_record.partial_writes = false
|
657
|
+
```
|
658
|
+
|
659
|
+
For Rails 7, use:
|
660
|
+
|
661
|
+
```ruby
|
662
|
+
config.active_record.partial_inserts = false
|
663
|
+
```
|
664
|
+
|
612
665
|
### Keeping non-unique indexes to three columns or less
|
613
666
|
|
614
667
|
#### Bad
|
@@ -8,6 +8,7 @@ module StrongMigrations
|
|
8
8
|
def initialize(migration)
|
9
9
|
@migration = migration
|
10
10
|
@new_tables = []
|
11
|
+
@new_columns = []
|
11
12
|
@safe = false
|
12
13
|
@timeouts_set = false
|
13
14
|
@committed = false
|
@@ -46,6 +47,8 @@ module StrongMigrations
|
|
46
47
|
check_add_reference(method, *args)
|
47
48
|
when :change_column
|
48
49
|
check_change_column(*args)
|
50
|
+
when :change_column_default
|
51
|
+
check_change_column_default(*args)
|
49
52
|
when :change_column_null
|
50
53
|
check_change_column_null(*args)
|
51
54
|
when :change_table
|
@@ -32,6 +32,9 @@ module StrongMigrations
|
|
32
32
|
table, column, type = args
|
33
33
|
default = options[:default]
|
34
34
|
|
35
|
+
# keep track of new columns of change_column_default check
|
36
|
+
@new_columns << [table.to_s, column.to_s]
|
37
|
+
|
35
38
|
# Check key since DEFAULT NULL behaves differently from no default
|
36
39
|
#
|
37
40
|
# Also, Active Record has special case for uuid columns that allows function default values
|
@@ -71,6 +74,10 @@ Then add the NOT NULL constraint in separate migrations."
|
|
71
74
|
raise_error :add_column_json,
|
72
75
|
command: command_str("add_column", [table, column, :jsonb, options])
|
73
76
|
end
|
77
|
+
|
78
|
+
if type.to_s == "virtual" && options[:stored]
|
79
|
+
raise_error :add_column_generated_stored, rewrite_blocks: adapter.rewrite_blocks
|
80
|
+
end
|
74
81
|
end
|
75
82
|
|
76
83
|
def check_add_exclusion_constraint(*args)
|
@@ -147,7 +154,7 @@ Then add the NOT NULL constraint in separate migrations."
|
|
147
154
|
if bad_index || options[:foreign_key]
|
148
155
|
if index_value.is_a?(Hash)
|
149
156
|
options[:index] = options[:index].merge(algorithm: :concurrently)
|
150
|
-
|
157
|
+
elsif index_value
|
151
158
|
options = options.merge(index: {algorithm: :concurrently})
|
152
159
|
end
|
153
160
|
|
@@ -194,6 +201,18 @@ Then add the foreign key in separate migrations."
|
|
194
201
|
raise_error :change_column, rewrite_blocks: adapter.rewrite_blocks unless safe
|
195
202
|
end
|
196
203
|
|
204
|
+
def check_change_column_default(*args)
|
205
|
+
table, column, _default_or_changes = args
|
206
|
+
|
207
|
+
# just check ActiveRecord::Base, even though can override on model
|
208
|
+
partial_inserts = ar_version >= 7 ? ActiveRecord::Base.partial_inserts : ActiveRecord::Base.partial_writes
|
209
|
+
|
210
|
+
if partial_inserts && !new_column?(table, column)
|
211
|
+
raise_error :change_column_default,
|
212
|
+
config: ar_version >= 7 ? "partial_inserts" : "partial_writes"
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
197
216
|
def check_change_column_null(*args)
|
198
217
|
table, column, null, default = args
|
199
218
|
if !null
|
@@ -456,5 +475,9 @@ Then add the foreign key in separate migrations."
|
|
456
475
|
def new_table?(table)
|
457
476
|
@new_tables.include?(table.to_s)
|
458
477
|
end
|
478
|
+
|
479
|
+
def new_column?(table, column)
|
480
|
+
new_table?(table) || @new_columns.include?([table.to_s, column.to_s])
|
481
|
+
end
|
459
482
|
end
|
460
483
|
end
|
@@ -50,6 +50,9 @@ class %{migration_name} < ActiveRecord::Migration%{migration_suffix}
|
|
50
50
|
end
|
51
51
|
end",
|
52
52
|
|
53
|
+
add_column_generated_stored:
|
54
|
+
"Adding a stored generated column blocks %{rewrite_blocks} while the entire table is rewritten.",
|
55
|
+
|
53
56
|
change_column:
|
54
57
|
"Changing the type of an existing column blocks %{rewrite_blocks}
|
55
58
|
while the entire table is rewritten. A safer approach is to:
|
@@ -158,6 +161,13 @@ Otherwise, remove the force option.",
|
|
158
161
|
execute call, so cannot help you here. Please make really sure that what
|
159
162
|
you're doing is safe before proceeding, then wrap it in a safety_assured { ... } block.",
|
160
163
|
|
164
|
+
change_column_default:
|
165
|
+
"Partial writes are enabled, which can cause incorrect values
|
166
|
+
to be inserted when changing the default value of a column.
|
167
|
+
Disable partial writes in config/application.rb:
|
168
|
+
|
169
|
+
config.active_record.%{config} = false",
|
170
|
+
|
161
171
|
change_column_null:
|
162
172
|
"Passing a default value to change_column_null runs a single UPDATE query,
|
163
173
|
which can cause downtime. Instead, backfill the existing rows in the
|
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.6.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-
|
13
|
+
date: 2023-07-22 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.
|
78
|
+
rubygems_version: 3.4.10
|
79
79
|
signing_key:
|
80
80
|
specification_version: 4
|
81
81
|
summary: Catch unsafe migrations in development
|