strong_migrations 1.4.4 → 1.6.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: 7444023bd5f0829a289ebc9d222a28f041a4e3789224438f9fd6bffa01fa6668
4
- data.tar.gz: feb5010616cc8ef6cf36088be7a67bb0d74272cde79d0e9a0a442272bfa92d99
3
+ metadata.gz: 23737504a1d3beb08bf2412b161b8e59a8a620b4f4d993f11fe931e7f1772452
4
+ data.tar.gz: 993dc5f9d78cba148540a22772c18ed115ea6ae8075dd209cc3a869cc2a647a4
5
5
  SHA512:
6
- metadata.gz: '0599a3fef628ec724471ef53b7ad7358831e6b638bdd0ca3b7cb79c1b416da6c044793ecca5475f5e602e8a7d1235ced3ff3fda526f31b9c4a56990c8531f9b4'
7
- data.tar.gz: 6aac91ecb348c6a04c25511adf7df5d3e97823f6b9acdfbfd86bac50c4f5856f15fdcc271f00d0fc6f1e5d2cfdbaf3da5e056b1476aaf9b224e7df89b9737e01
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
- else
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
@@ -1,3 +1,3 @@
1
1
  module StrongMigrations
2
- VERSION = "1.4.4"
2
+ VERSION = "1.6.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.4.4
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-03-08 00:00:00.000000000 Z
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.6
78
+ rubygems_version: 3.4.10
79
79
  signing_key:
80
80
  specification_version: 4
81
81
  summary: Catch unsafe migrations in development