strong_migrations 1.4.4 → 2.5.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 +86 -1
- data/LICENSE.txt +1 -1
- data/README.md +285 -155
- data/lib/generators/strong_migrations/install_generator.rb +3 -7
- data/lib/generators/strong_migrations/templates/initializer.rb.tt +3 -0
- data/lib/strong_migrations/adapters/abstract_adapter.rb +13 -10
- data/lib/strong_migrations/adapters/mariadb_adapter.rb +2 -2
- data/lib/strong_migrations/adapters/mysql_adapter.rb +10 -5
- data/lib/strong_migrations/adapters/postgresql_adapter.rb +25 -28
- data/lib/strong_migrations/checker.rb +109 -19
- data/lib/strong_migrations/checks.rb +130 -85
- data/lib/strong_migrations/error_messages.rb +61 -14
- data/lib/strong_migrations/migration.rb +12 -6
- data/lib/strong_migrations/{database_tasks.rb → migration_context.rb} +20 -3
- data/lib/strong_migrations/migrator.rb +7 -5
- data/lib/strong_migrations/safe_methods.rb +60 -40
- data/lib/strong_migrations/schema_dumper.rb +15 -4
- data/lib/strong_migrations/version.rb +1 -1
- data/lib/strong_migrations.rb +9 -6
- metadata +7 -11
@@ -1,10 +1,9 @@
|
|
1
1
|
module StrongMigrations
|
2
2
|
module SafeMethods
|
3
3
|
def safe_by_default_method?(method)
|
4
|
-
StrongMigrations.safe_by_default && [:add_index, :add_belongs_to, :add_reference, :remove_index, :add_foreign_key, :add_check_constraint, :change_column_null].include?(method)
|
4
|
+
StrongMigrations.safe_by_default && !version_safe? && [:add_index, :add_belongs_to, :add_reference, :remove_index, :add_foreign_key, :add_check_constraint, :change_column_null].include?(method)
|
5
5
|
end
|
6
6
|
|
7
|
-
# TODO check if invalid index with expected name exists and remove if needed
|
8
7
|
def safe_add_index(*args, **options)
|
9
8
|
disable_transaction
|
10
9
|
@migration.add_index(*args, **options.merge(algorithm: :concurrently))
|
@@ -30,10 +29,11 @@ module StrongMigrations
|
|
30
29
|
(ActiveRecord::Base.pluralize_table_names ? reference.to_s.pluralize : reference).to_sym
|
31
30
|
end
|
32
31
|
|
32
|
+
foreign_key_opts = foreign_key.is_a?(Hash) ? foreign_key.except(:to_table) : {}
|
33
33
|
if reference
|
34
|
-
@migration.add_foreign_key(table, name, column: "#{reference}_id")
|
34
|
+
@migration.add_foreign_key(table, name, column: "#{reference}_id", **foreign_key_opts)
|
35
35
|
else
|
36
|
-
@migration.add_foreign_key(table, name)
|
36
|
+
@migration.add_foreign_key(table, name, **foreign_key_opts)
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
@@ -46,22 +46,14 @@ module StrongMigrations
|
|
46
46
|
def safe_add_foreign_key(from_table, to_table, *args, **options)
|
47
47
|
@migration.reversible do |dir|
|
48
48
|
dir.up do
|
49
|
-
|
50
|
-
|
51
|
-
validate_options = options.slice(:column, :name)
|
52
|
-
if ActiveRecord::VERSION::MAJOR >= 6
|
53
|
-
@migration.validate_foreign_key(from_table, to_table, **validate_options)
|
54
|
-
else
|
55
|
-
@migration.validate_foreign_key(from_table, validate_options.any? ? validate_options : to_table)
|
49
|
+
if !connection.foreign_key_exists?(from_table, to_table, **options.merge(validate: false))
|
50
|
+
@migration.add_foreign_key(from_table, to_table, *args, **options.merge(validate: false))
|
56
51
|
end
|
52
|
+
disable_transaction
|
53
|
+
@migration.validate_foreign_key(from_table, to_table, **options.slice(:column, :name))
|
57
54
|
end
|
58
55
|
dir.down do
|
59
|
-
|
60
|
-
if ActiveRecord::VERSION::MAJOR >= 6
|
61
|
-
@migration.remove_foreign_key(from_table, to_table, **remove_options)
|
62
|
-
else
|
63
|
-
@migration.remove_foreign_key(from_table, remove_options.any? ? remove_options : to_table)
|
64
|
-
end
|
56
|
+
@migration.remove_foreign_key(from_table, to_table, **options.slice(:column, :name))
|
65
57
|
end
|
66
58
|
end
|
67
59
|
end
|
@@ -69,7 +61,10 @@ module StrongMigrations
|
|
69
61
|
def safe_add_check_constraint(table, expression, *args, add_options, validate_options)
|
70
62
|
@migration.reversible do |dir|
|
71
63
|
dir.up do
|
72
|
-
|
64
|
+
# only skip invalid constraints
|
65
|
+
unless connection.check_constraints(table).any? { |c| c.options[:name] == validate_options[:name] && !c.options[:validate] }
|
66
|
+
@migration.add_check_constraint(table, expression, *args, **add_options)
|
67
|
+
end
|
73
68
|
disable_transaction
|
74
69
|
@migration.validate_check_constraint(table, **validate_options)
|
75
70
|
end
|
@@ -79,35 +74,60 @@ module StrongMigrations
|
|
79
74
|
end
|
80
75
|
end
|
81
76
|
|
82
|
-
def safe_change_column_null(
|
77
|
+
def safe_change_column_null(add_args, validate_args, change_args, remove_args, table, column, default, constraints)
|
83
78
|
@migration.reversible do |dir|
|
84
79
|
dir.up do
|
85
80
|
unless default.nil?
|
86
|
-
|
87
|
-
|
81
|
+
# TODO search for parent model if needed
|
82
|
+
if connection.pool != ActiveRecord::Base.connection_pool
|
83
|
+
raise_error :change_column_null,
|
84
|
+
code: backfill_code(table, column, default)
|
85
|
+
end
|
86
|
+
|
87
|
+
model =
|
88
|
+
Class.new(ActiveRecord::Base) do
|
89
|
+
self.table_name = table
|
88
90
|
|
89
|
-
|
90
|
-
|
91
|
+
def self.to_s
|
92
|
+
"Backfill"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
update_sql =
|
97
|
+
model.connection_pool.with_connection do |c|
|
98
|
+
quoted_column = c.quote_column_name(column)
|
99
|
+
quoted_default = c.quote_default_expression(default, c.send(:column_for, table, column))
|
100
|
+
"#{quoted_column} = #{quoted_default}"
|
101
|
+
end
|
102
|
+
|
103
|
+
@migration.say("Backfilling default")
|
91
104
|
disable_transaction
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
@migration.change_column_null(*change_args)
|
96
|
-
@migration.safety_assured do
|
97
|
-
@migration.execute(remove_code)
|
105
|
+
model.unscoped.in_batches(of: 10000) do |relation|
|
106
|
+
relation.where(column => nil).update_all(update_sql)
|
107
|
+
sleep(0.01)
|
98
108
|
end
|
99
109
|
end
|
110
|
+
|
111
|
+
add_options = add_args.extract_options!
|
112
|
+
validate_options = validate_args.extract_options!
|
113
|
+
remove_options = remove_args.extract_options!
|
114
|
+
|
115
|
+
# only skip invalid constraints
|
116
|
+
unless constraints.any? { |c| c.options[:name] == validate_options[:name] && !c.options[:validate] }
|
117
|
+
@migration.add_check_constraint(*add_args, **add_options)
|
118
|
+
end
|
119
|
+
disable_transaction
|
120
|
+
|
121
|
+
connection.begin_db_transaction
|
122
|
+
@migration.validate_check_constraint(*validate_args, **validate_options)
|
123
|
+
@migration.change_column_null(*change_args)
|
124
|
+
@migration.remove_check_constraint(*remove_args, **remove_options)
|
125
|
+
connection.commit_db_transaction
|
100
126
|
end
|
101
127
|
dir.down do
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
@migration.change_column_null(*down_args)
|
106
|
-
else
|
107
|
-
@migration.safety_assured do
|
108
|
-
@migration.execute(remove_code)
|
109
|
-
end
|
110
|
-
end
|
128
|
+
down_args = change_args.dup
|
129
|
+
down_args[2] = true
|
130
|
+
@migration.change_column_null(*down_args)
|
111
131
|
end
|
112
132
|
end
|
113
133
|
end
|
@@ -116,13 +136,13 @@ module StrongMigrations
|
|
116
136
|
# so just commit at start
|
117
137
|
def disable_transaction
|
118
138
|
if in_transaction? && !transaction_disabled
|
119
|
-
|
139
|
+
connection.commit_db_transaction
|
120
140
|
self.transaction_disabled = true
|
121
141
|
end
|
122
142
|
end
|
123
143
|
|
124
144
|
def in_transaction?
|
125
|
-
|
145
|
+
connection.open_transactions > 0
|
126
146
|
end
|
127
147
|
end
|
128
148
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
module StrongMigrations
|
2
2
|
module SchemaDumper
|
3
|
-
def initialize(connection,
|
3
|
+
def initialize(connection, ...)
|
4
4
|
return super unless StrongMigrations.alphabetize_schema
|
5
5
|
|
6
|
-
super(WrappedConnection.new(connection),
|
6
|
+
super(WrappedConnection.new(connection), ...)
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
@@ -14,8 +14,19 @@ module StrongMigrations
|
|
14
14
|
@connection = connection
|
15
15
|
end
|
16
16
|
|
17
|
-
def columns(
|
18
|
-
@connection.columns(
|
17
|
+
def columns(...)
|
18
|
+
@connection.columns(...).sort_by(&:name)
|
19
|
+
end
|
20
|
+
|
21
|
+
# forward private methods with send
|
22
|
+
# method_missing cannot tell how method was called
|
23
|
+
# this is not ideal, but other solutions have drawbacks
|
24
|
+
def send(name, ...)
|
25
|
+
if respond_to?(name, true)
|
26
|
+
super
|
27
|
+
else
|
28
|
+
@connection.send(name, ...)
|
29
|
+
end
|
19
30
|
end
|
20
31
|
end
|
21
32
|
end
|
data/lib/strong_migrations.rb
CHANGED
@@ -11,8 +11,8 @@ require_relative "strong_migrations/adapters/postgresql_adapter"
|
|
11
11
|
require_relative "strong_migrations/checks"
|
12
12
|
require_relative "strong_migrations/safe_methods"
|
13
13
|
require_relative "strong_migrations/checker"
|
14
|
-
require_relative "strong_migrations/database_tasks"
|
15
14
|
require_relative "strong_migrations/migration"
|
15
|
+
require_relative "strong_migrations/migration_context"
|
16
16
|
require_relative "strong_migrations/migrator"
|
17
17
|
require_relative "strong_migrations/version"
|
18
18
|
|
@@ -29,7 +29,7 @@ module StrongMigrations
|
|
29
29
|
:target_postgresql_version, :target_mysql_version, :target_mariadb_version,
|
30
30
|
:enabled_checks, :lock_timeout, :statement_timeout, :check_down, :target_version,
|
31
31
|
:safe_by_default, :target_sql_mode, :lock_timeout_retries, :lock_timeout_retry_delay,
|
32
|
-
:alphabetize_schema
|
32
|
+
:alphabetize_schema, :skipped_databases, :remove_invalid_indexes, :transaction_timeout
|
33
33
|
attr_writer :lock_timeout_limit
|
34
34
|
end
|
35
35
|
self.auto_analyze = false
|
@@ -40,6 +40,8 @@ module StrongMigrations
|
|
40
40
|
self.safe_by_default = false
|
41
41
|
self.check_down = false
|
42
42
|
self.alphabetize_schema = false
|
43
|
+
self.skipped_databases = []
|
44
|
+
self.remove_invalid_indexes = false
|
43
45
|
|
44
46
|
# private
|
45
47
|
def self.developer_env?
|
@@ -83,6 +85,10 @@ module StrongMigrations
|
|
83
85
|
false
|
84
86
|
end
|
85
87
|
end
|
88
|
+
|
89
|
+
def self.skip_database(database)
|
90
|
+
self.skipped_databases << database
|
91
|
+
end
|
86
92
|
end
|
87
93
|
|
88
94
|
# load error messages
|
@@ -90,12 +96,9 @@ require_relative "strong_migrations/error_messages"
|
|
90
96
|
|
91
97
|
ActiveSupport.on_load(:active_record) do
|
92
98
|
ActiveRecord::Migration.prepend(StrongMigrations::Migration)
|
99
|
+
ActiveRecord::MigrationContext.prepend(StrongMigrations::MigrationContext)
|
93
100
|
ActiveRecord::Migrator.prepend(StrongMigrations::Migrator)
|
94
101
|
|
95
|
-
if defined?(ActiveRecord::Tasks::DatabaseTasks)
|
96
|
-
ActiveRecord::Tasks::DatabaseTasks.singleton_class.prepend(StrongMigrations::DatabaseTasks)
|
97
|
-
end
|
98
|
-
|
99
102
|
require_relative "strong_migrations/schema_dumper"
|
100
103
|
ActiveRecord::SchemaDumper.prepend(StrongMigrations::SchemaDumper)
|
101
104
|
end
|
metadata
CHANGED
@@ -1,16 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: strong_migrations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
- Bob Remeika
|
9
9
|
- David Waller
|
10
|
-
autorequire:
|
11
10
|
bindir: bin
|
12
11
|
cert_chain: []
|
13
|
-
date:
|
12
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
14
13
|
dependencies:
|
15
14
|
- !ruby/object:Gem::Dependency
|
16
15
|
name: activerecord
|
@@ -18,15 +17,14 @@ dependencies:
|
|
18
17
|
requirements:
|
19
18
|
- - ">="
|
20
19
|
- !ruby/object:Gem::Version
|
21
|
-
version: '
|
20
|
+
version: '7.1'
|
22
21
|
type: :runtime
|
23
22
|
prerelease: false
|
24
23
|
version_requirements: !ruby/object:Gem::Requirement
|
25
24
|
requirements:
|
26
25
|
- - ">="
|
27
26
|
- !ruby/object:Gem::Version
|
28
|
-
version: '
|
29
|
-
description:
|
27
|
+
version: '7.1'
|
30
28
|
email:
|
31
29
|
- andrew@ankane.org
|
32
30
|
- bob.remeika@gmail.com
|
@@ -47,9 +45,9 @@ files:
|
|
47
45
|
- lib/strong_migrations/adapters/postgresql_adapter.rb
|
48
46
|
- lib/strong_migrations/checker.rb
|
49
47
|
- lib/strong_migrations/checks.rb
|
50
|
-
- lib/strong_migrations/database_tasks.rb
|
51
48
|
- lib/strong_migrations/error_messages.rb
|
52
49
|
- lib/strong_migrations/migration.rb
|
50
|
+
- lib/strong_migrations/migration_context.rb
|
53
51
|
- lib/strong_migrations/migrator.rb
|
54
52
|
- lib/strong_migrations/railtie.rb
|
55
53
|
- lib/strong_migrations/safe_methods.rb
|
@@ -60,7 +58,6 @@ homepage: https://github.com/ankane/strong_migrations
|
|
60
58
|
licenses:
|
61
59
|
- MIT
|
62
60
|
metadata: {}
|
63
|
-
post_install_message:
|
64
61
|
rdoc_options: []
|
65
62
|
require_paths:
|
66
63
|
- lib
|
@@ -68,15 +65,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
68
65
|
requirements:
|
69
66
|
- - ">="
|
70
67
|
- !ruby/object:Gem::Version
|
71
|
-
version: '2
|
68
|
+
version: '3.2'
|
72
69
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
70
|
requirements:
|
74
71
|
- - ">="
|
75
72
|
- !ruby/object:Gem::Version
|
76
73
|
version: '0'
|
77
74
|
requirements: []
|
78
|
-
rubygems_version: 3.
|
79
|
-
signing_key:
|
75
|
+
rubygems_version: 3.6.9
|
80
76
|
specification_version: 4
|
81
77
|
summary: Catch unsafe migrations in development
|
82
78
|
test_files: []
|