online_migrations 0.5.1 → 0.5.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 95f3b31c9fe8edb868fade7dbdaed0ebf78d53da7a1ff52786a748663bc93bf5
4
- data.tar.gz: 94c2fed042d39993d85f6641db3b4a86c3a360b3c858f48aeb2da052bb2c52cc
3
+ metadata.gz: 0ac8a2eb2657fcc3536a9b56d16298f90f8e1a5e626ba52a6e9703b5bcaa7df0
4
+ data.tar.gz: c5357f94854fb43d1b8404ed9dcfafc2178430aeb9c6d69555f50629a94da3bd
5
5
  SHA512:
6
- metadata.gz: f844aa5e502a91739923039a8fb84d10ae9682404bd14bd4b92352400c6dae82963b21166178c72c47ea4ecbd0792b6f46da151d92ce205e217a5788c9a8b27a
7
- data.tar.gz: 2db40b6b3cf81f923251f16a023aedbb18f8f24d28abb9e6b638090030c013c24dd6a41942df3e4f4cd1264c84c997eaaeb6adcb1c2d8d8844bc4934406f9ebc
6
+ metadata.gz: 4ccbc274c76ab01f8ece559b3fee27c1c5c0c495225b393c3a4afb7941f0b61d3aab9ef2885195be78ed00f29bd5e6b18e3fe0cb044b745cd791aacf34283492
7
+ data.tar.gz: 04fb250e30e7620ac1bd06fddf24a5fdfe616f285422e47e19423e23be0c8117e57d45e45029a2e1212cc5e6ad9baf3605fac8b038152fdc73b2d9a818ba211c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  ## master (unreleased)
2
2
 
3
+ ## 0.5.3 (2022-11-10)
4
+
5
+ - Fix removing index by name
6
+ - Fix multiple databases support for `start_after` and `target_version` configs
7
+ - Fix error when `Rails` defined without `Rails.env`
8
+ - Improve error message for adding column with a NULL default for PostgreSQL < 11
9
+
10
+ ## 0.5.2 (2022-10-04)
11
+
12
+ - Fix sequence resetting in tests that use fixtures
13
+
14
+ - Fix `update_column_in_batches` for SQL subquery values
15
+
16
+ It generated inefficient queries before, e.g.:
17
+
18
+ ```ruby
19
+ update_column_in_batches(:users, :comments_count, Arel.sql(<<~SQL))
20
+ (select count(*) from comments where comments.user_id = users.id)
21
+ SQL
22
+ ```
23
+
24
+ Generated SQL queries before:
25
+ ```sql
26
+ update users
27
+ set comments_count = (..count subquery..)
28
+ where comments_count is null or comments_count != (..count subquery..)
29
+ ```
30
+
31
+ Generated SQL queries now:
32
+ ```sql
33
+ update users set comments_count = (..count subquery..)
34
+ ```
35
+
36
+ - Fix check for `add_column` with `default: nil` for PostgreSQL < 11
37
+ - Replacing a unique index when other unique index with the prefix of columns exists is safe
38
+
3
39
  ## 0.5.1 (2022-07-19)
4
40
 
5
41
  - Raise for possible index corruption in all environments (previously, the check was made only
@@ -10,21 +10,21 @@ module OnlineMigrations
10
10
 
11
11
  source_root File.expand_path("templates", __dir__)
12
12
 
13
- def create_migration_file
14
- migration_template("migration.rb", File.join(migrations_dir, "install_online_migrations.rb"))
15
- end
16
-
17
13
  def copy_initializer_file
18
14
  template("initializer.rb", "config/initializers/online_migrations.rb")
19
15
  end
20
16
 
17
+ def create_migration_file
18
+ migration_template("migration.rb", File.join(migrations_dir, "install_online_migrations.rb"))
19
+ end
20
+
21
21
  private
22
22
  def migration_parent
23
23
  Utils.migration_parent_string
24
24
  end
25
25
 
26
26
  def start_after
27
- self.class.next_migration_number(migrations_dir)
27
+ self.class.current_migration_number(migrations_dir)
28
28
  end
29
29
 
30
30
  def migrations_dir
@@ -59,11 +59,11 @@ OnlineMigrations.configure do |config|
59
59
 
60
60
  # Add custom checks. Use the `stop!` method to stop migrations.
61
61
  #
62
- # config.add_check do |method, args|
63
- # if method == :add_column && args[0].to_s == "users"
64
- # stop!("No more columns on the users table")
65
- # end
62
+ # config.add_check do |method, args|
63
+ # if method == :add_column && args[0].to_s == "users"
64
+ # stop!("No more columns on the users table")
66
65
  # end
66
+ # end
67
67
 
68
68
  # ==> Background migrations configuration
69
69
  # The number of rows to process in a single background migration run.
@@ -174,13 +174,18 @@ module OnlineMigrations
174
174
  def add_column(table_name, column_name, type, **options)
175
175
  default = options[:default]
176
176
  volatile_default = false
177
- if !new_or_small_table?(table_name) && !default.nil? &&
178
- (postgresql_version < Gem::Version.new("11") || (volatile_default = Utils.volatile_default?(connection, type, default)))
177
+ if !new_or_small_table?(table_name) && options.key?(:default) &&
178
+ (postgresql_version < Gem::Version.new("11") || (!default.nil? && (volatile_default = Utils.volatile_default?(connection, type, default))))
179
179
 
180
- raise_error :add_column_with_default,
181
- code: command_str(:add_column_with_default, table_name, column_name, type, options),
182
- not_null: options[:null] == false,
183
- volatile_default: volatile_default
180
+ if default.nil?
181
+ raise_error :add_column_with_default_null,
182
+ code: command_str(:add_column, table_name, column_name, type, options.except(:default))
183
+ else
184
+ raise_error :add_column_with_default,
185
+ code: command_str(:add_column_with_default, table_name, column_name, type, options),
186
+ not_null: options[:null] == false,
187
+ volatile_default: volatile_default
188
+ end
184
189
  end
185
190
 
186
191
  if type == :json
@@ -478,9 +483,17 @@ module OnlineMigrations
478
483
  command: command_str(:remove_index, table_name, **options.merge(algorithm: :concurrently))
479
484
  end
480
485
 
481
- if options[:column] || options[:name]
482
- options[:column] ||= connection.indexes(table_name).find { |index| index.name == options[:name].to_s }
483
- @removed_indexes << IndexDefinition.new(table: table_name, columns: options.delete(:column), **options)
486
+ index_def = connection.indexes(table_name).find do |index|
487
+ index.name == options[:name].to_s ||
488
+ Array(index.columns).map(&:to_s) == Array(options[:column]).map(&:to_s)
489
+ end
490
+
491
+ if index_def
492
+ existing_options = [:name, :columns, :unique, :where, :type, :using, :opclasses].map do |option|
493
+ [option, index_def.public_send(option)] if index_def.respond_to?(option)
494
+ end.compact.to_h
495
+
496
+ @removed_indexes << IndexDefinition.new(table: table_name, **existing_options)
484
497
  end
485
498
  end
486
499
 
@@ -185,7 +185,7 @@ module OnlineMigrations
185
185
  @small_tables = []
186
186
  @check_down = false
187
187
  @enabled_checks = @error_messages.keys.map { |k| [k, {}] }.to_h
188
- @verbose_sql_logs = defined?(Rails) && Rails.env.production?
188
+ @verbose_sql_logs = defined?(Rails.env) && Rails.env.production?
189
189
  end
190
190
 
191
191
  def lock_retrier=(value)
@@ -262,17 +262,13 @@ module OnlineMigrations
262
262
  private
263
263
  def ensure_supports_multiple_dbs
264
264
  unless Utils.supports_multiple_dbs?
265
- raise "Multiple databases are not supported by this ActiveRecord version"
265
+ raise "OnlineMigrations does not support multiple databases for Active Record < 6.1"
266
266
  end
267
267
  end
268
268
 
269
269
  def db_config_name
270
270
  connection = OnlineMigrations.current_migration.connection
271
- if Utils.ar_version < 6.1
272
- connection.pool.spec.name
273
- else
274
- connection.pool.db_config.name
275
- end
271
+ connection.pool.db_config.name
276
272
  end
277
273
  end
278
274
  end
@@ -74,6 +74,16 @@ class <%= migration_name %> < <%= migration_parent %>
74
74
  end
75
75
  <% end %>",
76
76
 
77
+ add_column_with_default_null:
78
+ "Adding a column with a null default blocks reads and writes while the entire table is rewritten.
79
+ Instead, add the column without a default value.
80
+
81
+ class <%= migration_name %> < <%= migration_parent %>
82
+ def change
83
+ <%= code %>
84
+ end
85
+ end",
86
+
77
87
  add_column_json:
78
88
  "There's no equality operator for the json column type, which can cause errors for
79
89
  existing SELECT DISTINCT queries in your application. Use jsonb instead.
@@ -30,10 +30,7 @@ module OnlineMigrations
30
30
  return false if where != other.where
31
31
  return false if other.respond_to?(:opclasses) && opclasses != other.opclasses
32
32
 
33
- case [unique, other.unique]
34
- when [true, true]
35
- columns == other.columns
36
- when [true, false]
33
+ if unique && !other.unique
37
34
  false
38
35
  else
39
36
  prefix?(self, other)
@@ -69,8 +69,9 @@ module OnlineMigrations
69
69
  def renamed_tables
70
70
  @renamed_tables ||= begin
71
71
  table_renames = OnlineMigrations.config.table_renames
72
+ views = connection.views
72
73
  table_renames.select do |old_name, _|
73
- connection.views.include?(old_name)
74
+ views.include?(old_name)
74
75
  end
75
76
  end
76
77
  end
@@ -78,8 +79,9 @@ module OnlineMigrations
78
79
  def renamed_columns
79
80
  @renamed_columns ||= begin
80
81
  column_renames = OnlineMigrations.config.column_renames
82
+ views = connection.views
81
83
  column_renames.select do |table_name, _|
82
- connection.views.include?(table_name)
84
+ views.include?(table_name)
83
85
  end
84
86
  end
85
87
  end
@@ -85,9 +85,13 @@ module OnlineMigrations
85
85
 
86
86
  conditions = columns_and_values.map do |(column_name, value)|
87
87
  value = Arel.sql(value.call.to_s) if value.is_a?(Proc)
88
- arel_column = model.arel_table[column_name]
89
- arel_column.not_eq(value).or(arel_column.eq(nil))
90
- end
88
+
89
+ # Ignore subqueries in conditions
90
+ unless value.is_a?(Arel::Nodes::SqlLiteral) && value.to_s =~ /select\s+/i
91
+ arel_column = model.arel_table[column_name]
92
+ arel_column.not_eq(value).or(arel_column.eq(nil))
93
+ end
94
+ end.compact
91
95
 
92
96
  batch_relation = model.where(conditions.inject(:and))
93
97
  batch_relation = yield batch_relation if block_given?
@@ -685,7 +689,18 @@ module OnlineMigrations
685
689
  index_name = options[:name]
686
690
  index_name ||= index_name(table_name, column_names)
687
691
 
688
- if index_exists?(table_name, column_names, **options)
692
+ index_exists =
693
+ if Utils.ar_version <= 5.0
694
+ # Older Active Record is unable to handle blank columns correctly in `index_exists?`,
695
+ # so we need to use `index_name_exists?`.
696
+ index_name_exists?(table_name, index_name, nil)
697
+ elsif Utils.ar_version <= 6.0
698
+ index_name_exists?(table_name, index_name)
699
+ else
700
+ index_exists?(table_name, column_names, **options)
701
+ end
702
+
703
+ if index_exists
689
704
  disable_statement_timeout do
690
705
  # "DROP INDEX CONCURRENTLY" requires a "SHARE UPDATE EXCLUSIVE" lock.
691
706
  # It only conflicts with constraint validations, other creating/removing indexes,
@@ -820,6 +835,29 @@ module OnlineMigrations
820
835
  end
821
836
  end
822
837
 
838
+ # @private
839
+ def pk_and_sequence_for(table)
840
+ views = self.views
841
+
842
+ table_renames = OnlineMigrations.config.table_renames
843
+ renamed_tables = table_renames.select do |old_name, _|
844
+ views.include?(old_name)
845
+ end
846
+
847
+ column_renames = OnlineMigrations.config.column_renames
848
+ renamed_columns = column_renames.select do |table_name, _|
849
+ views.include?(table_name)
850
+ end
851
+
852
+ if renamed_tables.key?(table)
853
+ super(renamed_tables[table])
854
+ elsif renamed_columns.key?(table)
855
+ super("#{table}_column_rename")
856
+ else
857
+ super
858
+ end
859
+ end
860
+
823
861
  # Disables statement timeout while executing &block
824
862
  #
825
863
  # Long-running migrations may take more than the timeout allowed by the database.
@@ -905,7 +943,7 @@ module OnlineMigrations
905
943
  def __index_column_names(column_names)
906
944
  if column_names.is_a?(String) && /\W/.match(column_names)
907
945
  column_names
908
- else
946
+ elsif column_names.present?
909
947
  Array(column_names)
910
948
  end
911
949
  end
@@ -9,7 +9,7 @@ module OnlineMigrations
9
9
  end
10
10
 
11
11
  def developer_env?
12
- defined?(Rails) && (Rails.env.development? || Rails.env.test?)
12
+ defined?(Rails.env) && (Rails.env.development? || Rails.env.test?)
13
13
  end
14
14
 
15
15
  def say(message)
@@ -26,7 +26,9 @@ module OnlineMigrations
26
26
  end
27
27
 
28
28
  def supports_multiple_dbs?
29
- ar_version >= 6.0
29
+ # Technically, Active Record 6.0+ supports multiple databases,
30
+ # but we can not get the database spec name for this version.
31
+ ar_version >= 6.1
30
32
  end
31
33
 
32
34
  def migration_parent
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OnlineMigrations
4
- VERSION = "0.5.1"
4
+ VERSION = "0.5.3"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: online_migrations
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - fatkodima
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-07-18 00:00:00.000000000 Z
11
+ date: 2022-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -101,7 +101,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
101
101
  - !ruby/object:Gem::Version
102
102
  version: '0'
103
103
  requirements: []
104
- rubygems_version: 3.3.7
104
+ rubygems_version: 3.1.6
105
105
  signing_key:
106
106
  specification_version: 4
107
107
  summary: Catch unsafe PostgreSQL migrations in development and run them easier in