strong_migrations 1.2.0 → 1.3.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: 4c0ea5c8cf3e904b89b8c4ab2f28ff60031515e40edf5b0c617ca4b8f8249650
4
- data.tar.gz: 5cb0fd63058bf618595cd929ef90de3cf90f02963f0adca21182c578f41c3320
3
+ metadata.gz: 4da88a3161110bc4e7fd6cc989ea51e0db2e32681c8026db11ac74c763f253b3
4
+ data.tar.gz: 583cd8b1e9665842ff6017c6d7242dcbfd9583157327b33ec023d5357c4e799f
5
5
  SHA512:
6
- metadata.gz: 50db9d240d49b60b75d97c76765b8f5e3b252c2cc826bd5e50e9b57311082dfea2c9cc5790fd905a2eac45a3eb6afb2784c7ed7962dcaf0172157548e1c8d233
7
- data.tar.gz: 2f2e5311cad061b712ee9dfb69a41b5e81f31be8db257bd095cbd8124bc461551afebb305d95b0bb090d6f7ef85c61618d61e64e9b73f266338a0aa7f5101b53
6
+ metadata.gz: 68356a9f5966f33a2a43f35a5f74ce21943798e4d85eb1c52710808dbac0efab6659d67a22d89aa4360cfc4f87e0dce6312e16f3a71dc0a739e87bb335d42f84
7
+ data.tar.gz: 67038470a20b75fd3dfb8642fea4775aedb08bff959255a44aa92271e2214300e9140f3d7cce21eae83e81ea314739c06999ce8d46a72f76f75f675e6c8914fb
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 1.3.0 (2022-08-30)
2
+
3
+ - Added check for `add_column` with `uuid` type and volatile default value
4
+
1
5
  ## 1.2.0 (2022-06-10)
2
6
 
3
7
  - Added check for index corruption with Postgres 14.0 to 14.3
data/README.md CHANGED
@@ -135,7 +135,7 @@ class AddSomeColumnToUsers < ActiveRecord::Migration[7.0]
135
135
  end
136
136
  ```
137
137
 
138
- In Postgres 11+, MySQL 8.0.12+, and MariaDB 10.3.2+, this no longer requires a table rewrite and is safe.
138
+ In Postgres 11+, MySQL 8.0.12+, and MariaDB 10.3.2+, this no longer requires a table rewrite and is safe (except for volatile functions like `gen_random_uuid()`).
139
139
 
140
140
  #### Good
141
141
 
@@ -681,7 +681,7 @@ Disable specific checks with:
681
681
  StrongMigrations.disable_check(:add_index)
682
682
  ```
683
683
 
684
- Check the [source code](https://github.com/ankane/strong_migrations/blob/master/lib/strong_migrations.rb) for the list of keys.
684
+ Check the [source code](https://github.com/ankane/strong_migrations/blob/master/lib/strong_migrations/error_messages.rb) for the list of keys.
685
685
 
686
686
  ## Down Migrations / Rollbacks
687
687
 
@@ -14,11 +14,7 @@ module StrongMigrations
14
14
  target_version(StrongMigrations.target_postgresql_version) do
15
15
  version = select_all("SHOW server_version_num").first["server_version_num"].to_i
16
16
  # major and minor version
17
- if version >= 100000
18
- "#{version / 10000}.#{(version % 10000)}"
19
- else
20
- "#{version / 10000}.#{(version % 10000) / 100}"
21
- end
17
+ "#{version / 10000}.#{(version % 10000)}"
22
18
  end
23
19
  end
24
20
  end
@@ -76,7 +72,7 @@ module StrongMigrations
76
72
  # but there doesn't seem to be a way to set/modify it
77
73
  # https://wiki.postgresql.org/wiki/What%27s_new_in_PostgreSQL_9.2#Reduce_ALTER_TABLE_rewrites
78
74
  when "numeric", "decimal"
79
- # numeric and decimal are equivalent and can be used interchangably
75
+ # numeric and decimal are equivalent and can be used interchangeably
80
76
  safe = ["numeric", "decimal"].include?(existing_type) &&
81
77
  (
82
78
  (
@@ -166,6 +162,13 @@ module StrongMigrations
166
162
  !StrongMigrations.developer_env?
167
163
  end
168
164
 
165
+ # default to true if unsure
166
+ def default_volatile?(default)
167
+ name = default.to_s.delete_suffix("()")
168
+ rows = select_all("SELECT provolatile FROM pg_proc WHERE proname = #{connection.quote(name)}").to_a
169
+ rows.empty? || rows.any? { |r| r["provolatile"] == "v" }
170
+ end
171
+
169
172
  private
170
173
 
171
174
  def set_timeout(setting, timeout)
@@ -32,7 +32,9 @@ module StrongMigrations
32
32
  table, column, type = args
33
33
  default = options[:default]
34
34
 
35
- if !default.nil? && !adapter.add_column_default_safe?
35
+ # Active Record has special case for uuid columns that allows function default values
36
+ # https://github.com/rails/rails/blob/v7.0.3.1/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb#L92-L93
37
+ if !default.nil? && (!adapter.add_column_default_safe? || (volatile = (postgresql? && type.to_s == "uuid" && default.to_s.include?("()") && adapter.default_volatile?(default))))
36
38
  if options[:null] == false
37
39
  options = options.except(:null)
38
40
  append = "
@@ -44,9 +46,10 @@ Then add the NOT NULL constraint in separate migrations."
44
46
  add_command: command_str("add_column", [table, column, type, options.except(:default)]),
45
47
  change_command: command_str("change_column_default", [table, column, default]),
46
48
  remove_command: command_str("remove_column", [table, column]),
47
- code: backfill_code(table, column, default),
49
+ code: backfill_code(table, column, default, volatile),
48
50
  append: append,
49
- rewrite_blocks: adapter.rewrite_blocks
51
+ rewrite_blocks: adapter.rewrite_blocks,
52
+ default_type: (volatile ? "volatile" : "non-null")
50
53
  elsif default.is_a?(Proc) && postgresql?
51
54
  # adding a column with a VOLATILE default is not safe
52
55
  # https://www.postgresql.org/docs/current/sql-altertable.html#SQL-ALTERTABLE-NOTES
@@ -226,9 +229,17 @@ Then add the foreign key in separate migrations."
226
229
  safety_assured_str(add_code)
227
230
  end
228
231
 
232
+ validate_constraint_code =
233
+ if safe_with_check_constraint
234
+ down_code = "#{add_constraint_code}\n #{command_str(:change_column_null, [table, column, true])}"
235
+ "def up\n #{validate_constraint_code}\n end\n\n def down\n #{down_code}\n end"
236
+ else
237
+ "def change\n #{validate_constraint_code}\n end"
238
+ end
239
+
229
240
  raise_error :change_column_null_postgresql,
230
241
  add_constraint_code: add_constraint_code,
231
- validate_constraint_code: "def change\n #{validate_constraint_code}\n end"
242
+ validate_constraint_code: validate_constraint_code
232
243
  end
233
244
  elsif mysql? || mariadb?
234
245
  unless adapter.strict_mode?
@@ -409,9 +420,14 @@ Then add the foreign key in separate migrations."
409
420
  "#{command} #{str_args.join(", ")}"
410
421
  end
411
422
 
412
- def backfill_code(table, column, default)
423
+ def backfill_code(table, column, default, function = false)
413
424
  model = table.to_s.classify
414
- "#{model}.unscoped.in_batches do |relation| \n relation.update_all #{column}: #{default.inspect}\n sleep(0.01)\n end"
425
+ if function
426
+ # update_all(column: Arel.sql(default)) also works in newer versions of Active Record
427
+ "#{model}.unscoped.in_batches do |relation| \n relation.where(#{column}: nil).update_all(\"#{column} = #{default}\")\n sleep(0.01)\n end"
428
+ else
429
+ "#{model}.unscoped.in_batches do |relation| \n relation.update_all #{column}: #{default.inspect}\n sleep(0.01)\n end"
430
+ end
415
431
  end
416
432
 
417
433
  def new_table?(table)
@@ -1,7 +1,7 @@
1
1
  module StrongMigrations
2
2
  self.error_messages = {
3
3
  add_column_default:
4
- "Adding a column with a non-null default blocks %{rewrite_blocks} while the entire table is rewritten.
4
+ "Adding a column with a %{default_type} default blocks %{rewrite_blocks} while the entire table is rewritten.
5
5
  Instead, add the column without a default value, then change the default.
6
6
 
7
7
  class %{migration_name} < ActiveRecord::Migration%{migration_suffix}
@@ -1,3 +1,3 @@
1
1
  module StrongMigrations
2
- VERSION = "1.2.0"
2
+ VERSION = "1.3.0"
3
3
  end
@@ -1,5 +1,5 @@
1
1
  namespace :strong_migrations do
2
- # https://www.pgrs.net/2008/03/13/alphabetize-schema-rb-columns/
2
+ # https://www.pgrs.net/2008/03/12/alphabetize-schema-rb-columns/
3
3
  task :alphabetize_columns do
4
4
  $stderr.puts "Dumping schema"
5
5
  ActiveRecord::Base.logger.level = Logger::INFO
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.2.0
4
+ version: 1.3.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: 2022-06-10 00:00:00.000000000 Z
13
+ date: 2022-08-30 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord