strong_migrations 1.2.0 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +2 -2
- data/lib/strong_migrations/adapters/postgresql_adapter.rb +9 -6
- data/lib/strong_migrations/checks.rb +32 -9
- data/lib/strong_migrations/error_messages.rb +1 -1
- data/lib/strong_migrations/version.rb +1 -1
- data/lib/tasks/strong_migrations.rake +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b330db9323d9942a9e24a5fdc3ab75931030f859505cb9eef2f428df840b4dcc
|
4
|
+
data.tar.gz: 8aa1acd2debbc49206cf9b3928c16e85d4f2f47140142d4e9b981d1d54254e12
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2bce01385b02f0c005cae82cda4325f5332b400949cc0c5814a25c4bc30f78ff0a66730179dc52458bae242aa5cc4d1eb6be755696c731521580b3554f3eee6c
|
7
|
+
data.tar.gz: 72bcd4096a69bd3c8afd78caa43651b10bf5924318635adeb52308724751fa9fdbaeaa59e07a690a4a0915524afbab3487a8f1db17086c18e0b742c0d3de6b28
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
## 1.3.1 (2022-09-21)
|
2
|
+
|
3
|
+
- Fixed check for `add_column` with `default: nil` with Postgres 10
|
4
|
+
|
5
|
+
## 1.3.0 (2022-08-30)
|
6
|
+
|
7
|
+
- Added check for `add_column` with `uuid` type and volatile default value
|
8
|
+
|
1
9
|
## 1.2.0 (2022-06-10)
|
2
10
|
|
3
11
|
- 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
|
-
|
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
|
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,11 @@ module StrongMigrations
|
|
32
32
|
table, column, type = args
|
33
33
|
default = options[:default]
|
34
34
|
|
35
|
-
|
35
|
+
# Check key since DEFAULT NULL behaves differently from no default
|
36
|
+
#
|
37
|
+
# Also, Active Record has special case for uuid columns that allows function default values
|
38
|
+
# https://github.com/rails/rails/blob/v7.0.3.1/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb#L92-L93
|
39
|
+
if options.key?(:default) && (!adapter.add_column_default_safe? || (volatile = (postgresql? && type.to_s == "uuid" && default.to_s.include?("()") && adapter.default_volatile?(default))))
|
36
40
|
if options[:null] == false
|
37
41
|
options = options.except(:null)
|
38
42
|
append = "
|
@@ -44,9 +48,10 @@ Then add the NOT NULL constraint in separate migrations."
|
|
44
48
|
add_command: command_str("add_column", [table, column, type, options.except(:default)]),
|
45
49
|
change_command: command_str("change_column_default", [table, column, default]),
|
46
50
|
remove_command: command_str("remove_column", [table, column]),
|
47
|
-
code: backfill_code(table, column, default),
|
51
|
+
code: backfill_code(table, column, default, volatile),
|
48
52
|
append: append,
|
49
|
-
rewrite_blocks: adapter.rewrite_blocks
|
53
|
+
rewrite_blocks: adapter.rewrite_blocks,
|
54
|
+
default_type: (volatile ? "volatile" : "non-null")
|
50
55
|
elsif default.is_a?(Proc) && postgresql?
|
51
56
|
# adding a column with a VOLATILE default is not safe
|
52
57
|
# https://www.postgresql.org/docs/current/sql-altertable.html#SQL-ALTERTABLE-NOTES
|
@@ -219,16 +224,22 @@ Then add the foreign key in separate migrations."
|
|
219
224
|
|
220
225
|
add_constraint_code =
|
221
226
|
if constraint_methods
|
222
|
-
#
|
223
|
-
expr_column = column.to_s =~ /\A[a-z0-9_]+\z/ ? column : connection.quote_column_name(column)
|
224
|
-
command_str(:add_check_constraint, [table, "#{expr_column} IS NOT NULL", {name: constraint_name, validate: false}])
|
227
|
+
command_str(:add_check_constraint, [table, "#{quote_column_if_needed(column)} IS NOT NULL", {name: constraint_name, validate: false}])
|
225
228
|
else
|
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:
|
242
|
+
validate_constraint_code: validate_constraint_code
|
232
243
|
end
|
233
244
|
elsif mysql? || mariadb?
|
234
245
|
unless adapter.strict_mode?
|
@@ -409,9 +420,21 @@ 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
|
-
|
425
|
+
if function
|
426
|
+
# update_all(column: Arel.sql(default)) also works in newer versions of Active Record
|
427
|
+
update_expr = "#{quote_column_if_needed(column)} = #{default}"
|
428
|
+
"#{model}.unscoped.in_batches do |relation| \n relation.where(#{column}: nil).update_all(#{update_expr.inspect})\n sleep(0.01)\n end"
|
429
|
+
else
|
430
|
+
"#{model}.unscoped.in_batches do |relation| \n relation.update_all #{column}: #{default.inspect}\n sleep(0.01)\n end"
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
# only quote when needed
|
435
|
+
# important! only use for display purposes
|
436
|
+
def quote_column_if_needed(column)
|
437
|
+
column.to_s =~ /\A[a-z0-9_]+\z/ ? column : connection.quote_column_name(column)
|
415
438
|
end
|
416
439
|
|
417
440
|
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
|
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,5 +1,5 @@
|
|
1
1
|
namespace :strong_migrations do
|
2
|
-
# https://www.pgrs.net/2008/03/
|
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.
|
4
|
+
version: 1.3.1
|
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-
|
13
|
+
date: 2022-09-21 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|