strong_migrations 1.8.0 → 2.3.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 +38 -0
- data/LICENSE.txt +1 -1
- data/README.md +91 -93
- data/lib/generators/strong_migrations/install_generator.rb +3 -7
- data/lib/strong_migrations/adapters/abstract_adapter.rb +5 -10
- data/lib/strong_migrations/adapters/mariadb_adapter.rb +1 -1
- data/lib/strong_migrations/adapters/mysql_adapter.rb +9 -4
- data/lib/strong_migrations/adapters/postgresql_adapter.rb +24 -21
- data/lib/strong_migrations/checker.rb +70 -4
- data/lib/strong_migrations/checks.rb +80 -80
- data/lib/strong_migrations/error_messages.rb +17 -11
- data/lib/strong_migrations/migration.rb +2 -1
- data/lib/strong_migrations/{database_tasks.rb → migration_context.rb} +20 -3
- data/lib/strong_migrations/migrator.rb +6 -4
- data/lib/strong_migrations/safe_methods.rb +59 -38
- 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))
|
|
@@ -47,22 +46,16 @@ module StrongMigrations
|
|
|
47
46
|
def safe_add_foreign_key(from_table, to_table, *args, **options)
|
|
48
47
|
@migration.reversible do |dir|
|
|
49
48
|
dir.up do
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
@migration.validate_foreign_key(from_table, to_table, **validate_options)
|
|
55
|
-
else
|
|
56
|
-
@migration.validate_foreign_key(from_table, validate_options.any? ? validate_options : to_table)
|
|
49
|
+
# validate option is unintentionally ignored for Active Record < 7.1
|
|
50
|
+
# https://github.com/rails/rails/pull/45896
|
|
51
|
+
if !connection.foreign_key_exists?(from_table, to_table, **options.merge(validate: false))
|
|
52
|
+
@migration.add_foreign_key(from_table, to_table, *args, **options.merge(validate: false))
|
|
57
53
|
end
|
|
54
|
+
disable_transaction
|
|
55
|
+
@migration.validate_foreign_key(from_table, to_table, **options.slice(:column, :name))
|
|
58
56
|
end
|
|
59
57
|
dir.down do
|
|
60
|
-
|
|
61
|
-
if ActiveRecord::VERSION::MAJOR >= 6
|
|
62
|
-
@migration.remove_foreign_key(from_table, to_table, **remove_options)
|
|
63
|
-
else
|
|
64
|
-
@migration.remove_foreign_key(from_table, remove_options.any? ? remove_options : to_table)
|
|
65
|
-
end
|
|
58
|
+
@migration.remove_foreign_key(from_table, to_table, **options.slice(:column, :name))
|
|
66
59
|
end
|
|
67
60
|
end
|
|
68
61
|
end
|
|
@@ -70,7 +63,10 @@ module StrongMigrations
|
|
|
70
63
|
def safe_add_check_constraint(table, expression, *args, add_options, validate_options)
|
|
71
64
|
@migration.reversible do |dir|
|
|
72
65
|
dir.up do
|
|
73
|
-
|
|
66
|
+
# only skip invalid constraints
|
|
67
|
+
unless connection.check_constraints(table).any? { |c| c.options[:name] == validate_options[:name] && !c.options[:validate] }
|
|
68
|
+
@migration.add_check_constraint(table, expression, *args, **add_options)
|
|
69
|
+
end
|
|
74
70
|
disable_transaction
|
|
75
71
|
@migration.validate_check_constraint(table, **validate_options)
|
|
76
72
|
end
|
|
@@ -80,35 +76,60 @@ module StrongMigrations
|
|
|
80
76
|
end
|
|
81
77
|
end
|
|
82
78
|
|
|
83
|
-
def safe_change_column_null(
|
|
79
|
+
def safe_change_column_null(add_args, validate_args, change_args, remove_args, table, column, default, constraints)
|
|
84
80
|
@migration.reversible do |dir|
|
|
85
81
|
dir.up do
|
|
86
82
|
unless default.nil?
|
|
87
|
-
|
|
88
|
-
|
|
83
|
+
# TODO search for parent model if needed
|
|
84
|
+
if connection.pool != ActiveRecord::Base.connection_pool
|
|
85
|
+
raise_error :change_column_null,
|
|
86
|
+
code: backfill_code(table, column, default)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
model =
|
|
90
|
+
Class.new(ActiveRecord::Base) do
|
|
91
|
+
self.table_name = table
|
|
89
92
|
|
|
90
|
-
|
|
91
|
-
|
|
93
|
+
def self.to_s
|
|
94
|
+
"Backfill"
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
update_sql =
|
|
99
|
+
model.connection_pool.with_connection do |c|
|
|
100
|
+
quoted_column = c.quote_column_name(column)
|
|
101
|
+
quoted_default = c.quote_default_expression(default, c.send(:column_for, table, column))
|
|
102
|
+
"#{quoted_column} = #{quoted_default}"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
@migration.say("Backfilling default")
|
|
92
106
|
disable_transaction
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
@migration.change_column_null(*change_args)
|
|
97
|
-
@migration.safety_assured do
|
|
98
|
-
@migration.execute(remove_code)
|
|
107
|
+
model.unscoped.in_batches(of: 10000) do |relation|
|
|
108
|
+
relation.where(column => nil).update_all(update_sql)
|
|
109
|
+
sleep(0.01)
|
|
99
110
|
end
|
|
100
111
|
end
|
|
112
|
+
|
|
113
|
+
add_options = add_args.extract_options!
|
|
114
|
+
validate_options = validate_args.extract_options!
|
|
115
|
+
remove_options = remove_args.extract_options!
|
|
116
|
+
|
|
117
|
+
# only skip invalid constraints
|
|
118
|
+
unless constraints.any? { |c| c.options[:name] == validate_options[:name] && !c.options[:validate] }
|
|
119
|
+
@migration.add_check_constraint(*add_args, **add_options)
|
|
120
|
+
end
|
|
121
|
+
disable_transaction
|
|
122
|
+
|
|
123
|
+
connection.begin_db_transaction
|
|
124
|
+
@migration.validate_check_constraint(*validate_args, **validate_options)
|
|
125
|
+
@migration.change_column_null(*change_args)
|
|
126
|
+
@migration.remove_check_constraint(*remove_args, **remove_options)
|
|
127
|
+
connection.commit_db_transaction
|
|
101
128
|
end
|
|
102
129
|
dir.down do
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
@migration.change_column_null(*down_args)
|
|
107
|
-
else
|
|
108
|
-
@migration.safety_assured do
|
|
109
|
-
@migration.execute(remove_code)
|
|
110
|
-
end
|
|
111
|
-
end
|
|
130
|
+
down_args = change_args.dup
|
|
131
|
+
down_args[2] = true
|
|
132
|
+
@migration.change_column_null(*down_args)
|
|
112
133
|
end
|
|
113
134
|
end
|
|
114
135
|
end
|
|
@@ -117,13 +138,13 @@ module StrongMigrations
|
|
|
117
138
|
# so just commit at start
|
|
118
139
|
def disable_transaction
|
|
119
140
|
if in_transaction? && !transaction_disabled
|
|
120
|
-
|
|
141
|
+
connection.commit_db_transaction
|
|
121
142
|
self.transaction_disabled = true
|
|
122
143
|
end
|
|
123
144
|
end
|
|
124
145
|
|
|
125
146
|
def in_transaction?
|
|
126
|
-
|
|
147
|
+
connection.open_transactions > 0
|
|
127
148
|
end
|
|
128
149
|
end
|
|
129
150
|
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
|
|
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.3.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: 2025-04-03 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'
|
|
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'
|
|
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: '
|
|
68
|
+
version: '3.1'
|
|
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.2
|
|
80
76
|
specification_version: 4
|
|
81
77
|
summary: Catch unsafe migrations in development
|
|
82
78
|
test_files: []
|