online_migrations 0.6.0 → 0.7.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 +7 -0
- data/lib/online_migrations/background_migrations/migration.rb +8 -0
- data/lib/online_migrations/background_migrations/migration_job.rb +1 -0
- data/lib/online_migrations/background_migrations/migration_runner.rb +1 -0
- data/lib/online_migrations/change_column_type_helpers.rb +1 -1
- data/lib/online_migrations/command_recorder.rb +16 -1
- data/lib/online_migrations/schema_cache.rb +13 -11
- data/lib/online_migrations/schema_statements.rb +106 -25
- data/lib/online_migrations/version.rb +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: '029dd11fe1935f72c4933c081b45c972613caa252c578a005b34a902122923d6'
|
4
|
+
data.tar.gz: 04d66e91c1c54f1fe19d8a743d4b6f70ef218bbe2310eb58a779602a92e72736
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '07865cb5068a3cba7956db786f78d0999070b9445798af0df17f9360fa9b3e5b3f19409756f383b9eb402fa8cfa0a2b2bb6ca57475267f915844a248598004b5'
|
7
|
+
data.tar.gz: 0f44b2cd5bd9bad6f85c2980da2ea41ad79c100c46bdce0c4be2d9630cc239b42a0217ae07fd6f568503a42a640da6b63a2840d3b714994fce65708bd8dbf289
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
## master (unreleased)
|
2
2
|
|
3
|
+
## 0.7.0 (2023-02-14)
|
4
|
+
|
5
|
+
- Add support for renaming multiple columns at once in the same table
|
6
|
+
- Fix quoting table/column names across the library
|
7
|
+
- Fix deffered foreign keys support in `add_foreign_key` (Active Record >= 7)
|
8
|
+
- Reset attempts of failing jobs before executing background migration inline
|
9
|
+
|
3
10
|
## 0.6.0 (2023-02-04)
|
4
11
|
|
5
12
|
- Ignore internal Active Record migrations compatibility related options when suggesting a safe column type change
|
@@ -123,6 +123,14 @@ module OnlineMigrations
|
|
123
123
|
end
|
124
124
|
end
|
125
125
|
|
126
|
+
# @private
|
127
|
+
def reset_failed_jobs_attempts
|
128
|
+
iterator = BatchIterator.new(migration_jobs.failed.attempts_exceeded)
|
129
|
+
iterator.each_batch(of: 100) do |relation|
|
130
|
+
relation.update_all(attempts: 0)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
126
134
|
# @private
|
127
135
|
def next_batch_range
|
128
136
|
iterator = BatchIterator.new(migration_relation)
|
@@ -570,7 +570,7 @@ module OnlineMigrations
|
|
570
570
|
transaction do
|
571
571
|
# We'll need ACCESS EXCLUSIVE lock on the related tables,
|
572
572
|
# lets make sure it can be acquired from the start.
|
573
|
-
execute("LOCK TABLE #{table_name}, #{referencing_table_name} IN ACCESS EXCLUSIVE MODE")
|
573
|
+
execute("LOCK TABLE #{quote_table_name(table_name)}, #{quote_table_name(referencing_table_name)} IN ACCESS EXCLUSIVE MODE")
|
574
574
|
|
575
575
|
remove_foreign_key(referencing_table_name, name: existing_name)
|
576
576
|
__rename_constraint(referencing_table_name, tmp_name, existing_name)
|
@@ -6,9 +6,13 @@ module OnlineMigrations
|
|
6
6
|
REVERSIBLE_AND_IRREVERSIBLE_METHODS = [
|
7
7
|
:update_column_in_batches,
|
8
8
|
:initialize_column_rename,
|
9
|
+
:initialize_columns_rename,
|
9
10
|
:revert_initialize_column_rename,
|
11
|
+
:revert_initialize_columns_rename,
|
10
12
|
:finalize_column_rename,
|
13
|
+
:finalize_columns_rename,
|
11
14
|
:revert_finalize_column_rename,
|
15
|
+
:revert_finalize_columns_rename,
|
12
16
|
:initialize_table_rename,
|
13
17
|
:revert_initialize_table_rename,
|
14
18
|
:finalize_table_rename,
|
@@ -49,7 +53,9 @@ module OnlineMigrations
|
|
49
53
|
module StraightReversions
|
50
54
|
{
|
51
55
|
initialize_column_rename: :revert_initialize_column_rename,
|
56
|
+
initialize_columns_rename: :revert_initialize_columns_rename,
|
52
57
|
finalize_column_rename: :revert_finalize_column_rename,
|
58
|
+
finalize_columns_rename: :revert_finalize_columns_rename,
|
53
59
|
initialize_table_rename: :revert_initialize_table_rename,
|
54
60
|
finalize_table_rename: :revert_finalize_table_rename,
|
55
61
|
add_not_null_constraint: :remove_not_null_constraint,
|
@@ -84,11 +90,20 @@ module OnlineMigrations
|
|
84
90
|
_table, column, new_column = args
|
85
91
|
if !column || !new_column
|
86
92
|
raise ActiveRecord::IrreversibleMigration,
|
87
|
-
"
|
93
|
+
"revert_initialize_column_rename is only reversible if given a column and new_column."
|
88
94
|
end
|
89
95
|
[:initialize_column_rename, args]
|
90
96
|
end
|
91
97
|
|
98
|
+
def invert_revert_initialize_columns_rename(args)
|
99
|
+
_table, old_new_column_hash = args
|
100
|
+
unless old_new_column_hash
|
101
|
+
raise ActiveRecord::IrreversibleMigration,
|
102
|
+
"revert_initialize_columns_rename is only reversible if given a hash of old and new columns."
|
103
|
+
end
|
104
|
+
[:initialize_columns_rename, args]
|
105
|
+
end
|
106
|
+
|
92
107
|
def invert_finalize_table_rename(args)
|
93
108
|
_table_name, new_name = args
|
94
109
|
unless new_name
|
@@ -18,17 +18,10 @@ module OnlineMigrations
|
|
18
18
|
super(renamed_tables[table_name])
|
19
19
|
elsif renamed_columns.key?(table_name)
|
20
20
|
columns = super(column_rename_table(table_name))
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
new_column = old_column.dup
|
26
|
-
|
27
|
-
# ActiveRecord defines only reader for :name
|
28
|
-
new_column.instance_variable_set(:@name, new_column_name)
|
29
|
-
|
30
|
-
# Correspond to the ActiveRecord freezing of each column
|
31
|
-
columns << new_column.freeze
|
21
|
+
renamed_columns[table_name].each do |old_column_name, new_column_name|
|
22
|
+
duplicate_column(old_column_name, new_column_name, columns)
|
23
|
+
end
|
24
|
+
columns
|
32
25
|
else
|
33
26
|
super.reject { |column| column.name.end_with?("_for_type_change") }
|
34
27
|
end
|
@@ -94,5 +87,14 @@ module OnlineMigrations
|
|
94
87
|
@renamed_columns = nil
|
95
88
|
@renamed_tables = nil
|
96
89
|
end
|
90
|
+
|
91
|
+
def duplicate_column(old_column_name, new_column_name, columns)
|
92
|
+
old_column = columns.find { |column| column.name == old_column_name }
|
93
|
+
new_column = old_column.dup
|
94
|
+
# ActiveRecord defines only reader for :name
|
95
|
+
new_column.instance_variable_set(:@name, new_column_name)
|
96
|
+
# Correspond to the ActiveRecord freezing of each column
|
97
|
+
columns << new_column.freeze
|
98
|
+
end
|
97
99
|
end
|
98
100
|
end
|
@@ -177,20 +177,34 @@ module OnlineMigrations
|
|
177
177
|
# until `finalize_column_rename` is run
|
178
178
|
#
|
179
179
|
def initialize_column_rename(table_name, column_name, new_column_name)
|
180
|
-
|
180
|
+
initialize_columns_rename(table_name, { column_name => new_column_name })
|
181
|
+
end
|
181
182
|
|
183
|
+
# Same as `initialize_column_rename` but for multiple columns.
|
184
|
+
#
|
185
|
+
# This is useful to avoid multiple iterations of the safe column rename steps
|
186
|
+
# when renaming multiple columns.
|
187
|
+
#
|
188
|
+
# @param table_name [String, Symbol] table name
|
189
|
+
# @param old_new_column_hash [Hash] the hash of old and new columns
|
190
|
+
#
|
191
|
+
# @example
|
192
|
+
# initialize_columns_rename(:users, { fname: :first_name, lname: :last_name })
|
193
|
+
#
|
194
|
+
# @see #initialize_column_rename
|
195
|
+
#
|
196
|
+
def initialize_columns_rename(table_name, old_new_column_hash)
|
182
197
|
transaction do
|
183
|
-
|
184
|
-
execute("CREATE VIEW #{table_name} AS SELECT *, #{column_name} AS #{new_column_name} FROM #{tmp_table}")
|
198
|
+
rename_table_create_view(table_name, old_new_column_hash)
|
185
199
|
end
|
186
200
|
end
|
187
201
|
|
188
202
|
# Reverts operations performed by initialize_column_rename
|
189
203
|
#
|
190
204
|
# @param table_name [String, Symbol] table name
|
191
|
-
# @param
|
205
|
+
# @param column_name [String, Symbol] the name of the column to be renamed.
|
192
206
|
# Passing this argument will make this change reversible in migration
|
193
|
-
# @param
|
207
|
+
# @param new_column_name [String, Symbol] new new name of the column.
|
194
208
|
# Passing this argument will make this change reversible in migration
|
195
209
|
#
|
196
210
|
# @return [void]
|
@@ -198,9 +212,24 @@ module OnlineMigrations
|
|
198
212
|
# @example
|
199
213
|
# revert_initialize_column_rename(:users, :name, :first_name)
|
200
214
|
#
|
201
|
-
def revert_initialize_column_rename(table_name,
|
215
|
+
def revert_initialize_column_rename(table_name, column_name = nil, new_column_name = nil)
|
216
|
+
revert_initialize_columns_rename(table_name, { column_name => new_column_name })
|
217
|
+
end
|
218
|
+
|
219
|
+
# Same as `revert_initialize_column_rename` but for multiple columns.
|
220
|
+
#
|
221
|
+
# @param table_name [String, Symbol] table name
|
222
|
+
# @param _old_new_column_hash [Hash] the hash of old and new columns
|
223
|
+
# Passing this argument will make this change reversible in migration
|
224
|
+
#
|
225
|
+
# @return [void]
|
226
|
+
#
|
227
|
+
# @example
|
228
|
+
# revert_initialize_columns_rename(:users, { fname: :first_name, lname: :last_name })
|
229
|
+
#
|
230
|
+
def revert_initialize_columns_rename(table_name, _old_new_column_hash = nil)
|
202
231
|
transaction do
|
203
|
-
execute("DROP VIEW #{table_name}")
|
232
|
+
execute("DROP VIEW #{quote_table_name(table_name)}")
|
204
233
|
rename_table("#{table_name}_column_rename", table_name)
|
205
234
|
end
|
206
235
|
end
|
@@ -214,10 +243,24 @@ module OnlineMigrations
|
|
214
243
|
# finalize_column_rename(:users, :name, :first_name)
|
215
244
|
#
|
216
245
|
def finalize_column_rename(table_name, column_name, new_column_name)
|
246
|
+
finalize_columns_rename(table_name, { column_name => new_column_name })
|
247
|
+
end
|
248
|
+
|
249
|
+
# Same as `finalize_column_rename` but for multiple columns.
|
250
|
+
#
|
251
|
+
# @param (see #initialize_columns_rename)
|
252
|
+
# @return [void]
|
253
|
+
#
|
254
|
+
# @example
|
255
|
+
# finalize_columns_rename(:users, { fname: :first_name, lname: :last_name })
|
256
|
+
#
|
257
|
+
def finalize_columns_rename(table_name, old_new_column_hash)
|
217
258
|
transaction do
|
218
|
-
execute("DROP VIEW #{table_name}")
|
259
|
+
execute("DROP VIEW #{quote_table_name(table_name)}")
|
219
260
|
rename_table("#{table_name}_column_rename", table_name)
|
220
|
-
|
261
|
+
old_new_column_hash.each do |column_name, new_column_name|
|
262
|
+
rename_column(table_name, column_name, new_column_name)
|
263
|
+
end
|
221
264
|
end
|
222
265
|
end
|
223
266
|
|
@@ -230,12 +273,23 @@ module OnlineMigrations
|
|
230
273
|
# revert_finalize_column_rename(:users, :name, :first_name)
|
231
274
|
#
|
232
275
|
def revert_finalize_column_rename(table_name, column_name, new_column_name)
|
233
|
-
|
276
|
+
revert_finalize_columns_rename(table_name, { column_name => new_column_name })
|
277
|
+
end
|
234
278
|
|
279
|
+
# Same as `revert_finalize_column_rename` but for multiple columns.
|
280
|
+
#
|
281
|
+
# @param (see #initialize_columns_rename)
|
282
|
+
# @return [void]
|
283
|
+
#
|
284
|
+
# @example
|
285
|
+
# revert_finalize_columns_rename(:users, { fname: :first_name, lname: :last_name })
|
286
|
+
#
|
287
|
+
def revert_finalize_columns_rename(table_name, old_new_column_hash)
|
235
288
|
transaction do
|
236
|
-
|
237
|
-
|
238
|
-
|
289
|
+
old_new_column_hash.each do |column_name, new_column_name|
|
290
|
+
rename_column(table_name, new_column_name, column_name)
|
291
|
+
end
|
292
|
+
rename_table_create_view(table_name, old_new_column_hash)
|
239
293
|
end
|
240
294
|
end
|
241
295
|
|
@@ -286,7 +340,7 @@ module OnlineMigrations
|
|
286
340
|
def initialize_table_rename(table_name, new_name)
|
287
341
|
transaction do
|
288
342
|
rename_table(table_name, new_name)
|
289
|
-
execute("CREATE VIEW #{table_name} AS SELECT * FROM #{new_name}")
|
343
|
+
execute("CREATE VIEW #{quote_table_name(table_name)} AS SELECT * FROM #{quote_table_name(new_name)}")
|
290
344
|
end
|
291
345
|
end
|
292
346
|
|
@@ -300,7 +354,7 @@ module OnlineMigrations
|
|
300
354
|
#
|
301
355
|
def revert_initialize_table_rename(table_name, new_name)
|
302
356
|
transaction do
|
303
|
-
execute("DROP VIEW IF EXISTS #{table_name}")
|
357
|
+
execute("DROP VIEW IF EXISTS #{quote_table_name(table_name)}")
|
304
358
|
rename_table(new_name, table_name)
|
305
359
|
end
|
306
360
|
end
|
@@ -316,7 +370,7 @@ module OnlineMigrations
|
|
316
370
|
# finalize_table_rename(:users, :clients)
|
317
371
|
#
|
318
372
|
def finalize_table_rename(table_name, _new_name = nil)
|
319
|
-
execute("DROP VIEW IF EXISTS #{table_name}")
|
373
|
+
execute("DROP VIEW IF EXISTS #{quote_table_name(table_name)}")
|
320
374
|
end
|
321
375
|
|
322
376
|
# Reverts operations performed by finalize_table_rename
|
@@ -329,7 +383,7 @@ module OnlineMigrations
|
|
329
383
|
# revert_finalize_table_rename(:users, :clients)
|
330
384
|
#
|
331
385
|
def revert_finalize_table_rename(table_name, new_name)
|
332
|
-
execute("CREATE VIEW #{table_name} AS SELECT * FROM #{new_name}")
|
386
|
+
execute("CREATE VIEW #{quote_table_name(table_name)} AS SELECT * FROM #{quote_table_name(new_name)}")
|
333
387
|
end
|
334
388
|
|
335
389
|
# Swaps two column names in a table
|
@@ -738,14 +792,18 @@ module OnlineMigrations
|
|
738
792
|
options[:name] ||= __foreign_key_name(to_table, options[:column])
|
739
793
|
|
740
794
|
query = <<-SQL.strip_heredoc.dup
|
741
|
-
ALTER TABLE #{from_table}
|
742
|
-
ADD CONSTRAINT #{options[:name]}
|
743
|
-
FOREIGN KEY (#{options[:column]})
|
744
|
-
REFERENCES #{to_table} (#{options[:primary_key]})
|
795
|
+
ALTER TABLE #{quote_table_name(from_table)}
|
796
|
+
ADD CONSTRAINT #{quote_column_name(options[:name])}
|
797
|
+
FOREIGN KEY (#{quote_column_name(options[:column])})
|
798
|
+
REFERENCES #{quote_table_name(to_table)} (#{quote_column_name(options[:primary_key])})
|
745
799
|
SQL
|
746
800
|
query << "#{__action_sql('DELETE', options[:on_delete])}\n" if options[:on_delete].present?
|
747
801
|
query << "#{__action_sql('UPDATE', options[:on_update])}\n" if options[:on_update].present?
|
748
802
|
query << "NOT VALID\n" if !validate
|
803
|
+
if Utils.ar_version >= 7.0 && options[:deferrable]
|
804
|
+
query << " DEFERRABLE"
|
805
|
+
query << " INITIALLY #{options[:deferrable].to_s.upcase}\n" if options[:deferrable] != true
|
806
|
+
end
|
749
807
|
|
750
808
|
execute(query.squish)
|
751
809
|
end
|
@@ -766,7 +824,7 @@ module OnlineMigrations
|
|
766
824
|
# "VALIDATE CONSTRAINT" requires a "SHARE UPDATE EXCLUSIVE" lock.
|
767
825
|
# It only conflicts with other validations, creating/removing indexes,
|
768
826
|
# and some other "ALTER TABLE"s.
|
769
|
-
execute("ALTER TABLE #{from_table} VALIDATE CONSTRAINT #{fk_name_to_validate}")
|
827
|
+
execute("ALTER TABLE #{quote_table_name(from_table)} VALIDATE CONSTRAINT #{quote_column_name(fk_name_to_validate)}")
|
770
828
|
end
|
771
829
|
end
|
772
830
|
|
@@ -786,7 +844,10 @@ module OnlineMigrations
|
|
786
844
|
Utils.say("Check constraint was not created because it already exists (this may be due to an aborted migration " \
|
787
845
|
"or similar) table_name: #{table_name}, expression: #{expression}, constraint name: #{constraint_name}")
|
788
846
|
else
|
789
|
-
query =
|
847
|
+
query = <<-SQL.squish
|
848
|
+
ALTER TABLE #{quote_table_name(table_name)}
|
849
|
+
ADD CONSTRAINT #{quote_column_name(constraint_name)} CHECK (#{expression})
|
850
|
+
SQL
|
790
851
|
query += " NOT VALID" if !validate
|
791
852
|
|
792
853
|
execute(query)
|
@@ -808,7 +869,10 @@ module OnlineMigrations
|
|
808
869
|
# "VALIDATE CONSTRAINT" requires a "SHARE UPDATE EXCLUSIVE" lock.
|
809
870
|
# It only conflicts with other validations, creating/removing indexes,
|
810
871
|
# and some other "ALTER TABLE"s.
|
811
|
-
execute(
|
872
|
+
execute(<<-SQL.squish)
|
873
|
+
ALTER TABLE #{quote_table_name(table_name)}
|
874
|
+
VALIDATE CONSTRAINT #{quote_column_name(constraint_name)}
|
875
|
+
SQL
|
812
876
|
end
|
813
877
|
end
|
814
878
|
|
@@ -818,7 +882,10 @@ module OnlineMigrations
|
|
818
882
|
#
|
819
883
|
def remove_check_constraint(table_name, expression = nil, **options)
|
820
884
|
constraint_name = __check_constraint_name!(table_name, expression: expression, **options)
|
821
|
-
execute(
|
885
|
+
execute(<<-SQL.squish)
|
886
|
+
ALTER TABLE #{quote_table_name(table_name)}
|
887
|
+
DROP CONSTRAINT #{quote_column_name(constraint_name)}
|
888
|
+
SQL
|
822
889
|
end
|
823
890
|
end
|
824
891
|
|
@@ -1084,5 +1151,19 @@ module OnlineMigrations
|
|
1084
1151
|
_, schema = table_name.to_s.split(".").reverse
|
1085
1152
|
schema ? quote(schema) : "current_schema()"
|
1086
1153
|
end
|
1154
|
+
|
1155
|
+
def rename_table_create_view(table_name, old_new_column_hash)
|
1156
|
+
tmp_table = "#{table_name}_column_rename"
|
1157
|
+
rename_table(table_name, tmp_table)
|
1158
|
+
column_mapping = old_new_column_hash.map do |column_name, new_column_name|
|
1159
|
+
"#{quote_column_name(column_name)} AS #{quote_column_name(new_column_name)}"
|
1160
|
+
end.join(", ")
|
1161
|
+
|
1162
|
+
execute(<<-SQL.squish)
|
1163
|
+
CREATE VIEW #{quote_table_name(table_name)} AS
|
1164
|
+
SELECT *, #{column_mapping}
|
1165
|
+
FROM #{quote_table_name(tmp_table)}
|
1166
|
+
SQL
|
1167
|
+
end
|
1087
1168
|
end
|
1088
1169
|
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.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- fatkodima
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-02-
|
11
|
+
date: 2023-02-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|