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.
@@ -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
- @migration.add_foreign_key(from_table, to_table, *args, **options.merge(validate: false))
50
- disable_transaction
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
- remove_options = options.slice(:column, :name)
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
- @migration.add_check_constraint(table, expression, *args, **add_options)
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(add_code, validate_code, change_args, remove_code, default)
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
- raise Error, "default value not supported yet with safe_by_default"
87
- end
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
- @migration.safety_assured do
90
- @migration.execute(add_code)
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
- @migration.execute(validate_code)
93
- end
94
- if change_args
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
- if change_args
103
- down_args = change_args.dup
104
- down_args[2] = true
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
- @migration.connection.commit_db_transaction
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
- @migration.connection.open_transactions > 0
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, *args, **options)
3
+ def initialize(connection, ...)
4
4
  return super unless StrongMigrations.alphabetize_schema
5
5
 
6
- super(WrappedConnection.new(connection), *args, **options)
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(*args, **options)
18
- @connection.columns(*args, **options).sort_by(&:name)
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
@@ -1,3 +1,3 @@
1
1
  module StrongMigrations
2
- VERSION = "1.4.4"
2
+ VERSION = "2.5.0"
3
3
  end
@@ -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: 1.4.4
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: 2023-03-08 00:00:00.000000000 Z
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: '5.2'
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: '5.2'
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.6'
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.4.6
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: []