online_migrations 0.5.4 → 0.6.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
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 625229a6ef3cc2b9a6a6201ae2033b4560ca395482d797115563abd1dace6716
|
4
|
+
data.tar.gz: bc24c264f8a6be48e9232959a6e654e5a814c9872317d88e788eca8260dd6a7e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8054e3f5fec6fbc066cc3d6184a3002a2e77b21dee5ee7adca1025fa907c3e2263fae206b25150b662e37cd1dfac46cd66a54c82d725848020c30ed0cecef5c9
|
7
|
+
data.tar.gz: 2ad342552fbf328c341875192aba31c441b2bd5a72d3fac9edfef9caa9fa2eab003c9ae7d441d4778b3425f82aed140290de82df3291c030e0b1e7867871ae47
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,13 @@
|
|
1
1
|
## master (unreleased)
|
2
2
|
|
3
|
-
## 0.
|
3
|
+
## 0.6.0 (2023-02-04)
|
4
|
+
|
5
|
+
- Ignore internal Active Record migrations compatibility related options when suggesting a safe column type change
|
6
|
+
- Added check for `add_exclusion_constraint`
|
7
|
+
- Fix preserving old column options (`:comment` and `:collation`) when changing column type
|
8
|
+
- Set `NOT NULL` during new column creation when changing column type for PostgreSQL >= 11
|
9
|
+
|
10
|
+
## 0.5.4 (2023-01-03)
|
4
11
|
|
5
12
|
- Support ruby 3.2.0
|
6
13
|
|
data/README.md
CHANGED
@@ -20,6 +20,8 @@ See [comparison to `strong_migrations`](#comparison-to-strong_migrations)
|
|
20
20
|
- Rails 4.2+
|
21
21
|
- PostgreSQL 9.6+
|
22
22
|
|
23
|
+
**Note**: Since some migration helpers use database `VIEW`s to implement their logic, it is recommended to use `structure.sql` schema format, or otherwise add some gem (like [scenic](https://github.com/scenic-views/scenic)) to be able to dump them into the `schema.rb`.
|
24
|
+
|
23
25
|
## Installation
|
24
26
|
|
25
27
|
Add this line to your application's Gemfile:
|
@@ -140,6 +142,7 @@ Potentially dangerous operations:
|
|
140
142
|
- [replacing an index](#replacing-an-index)
|
141
143
|
- [adding a reference](#adding-a-reference)
|
142
144
|
- [adding a foreign key](#adding-a-foreign-key)
|
145
|
+
- [adding an exclusion constraint](#adding-an-exclusion-constraint)
|
143
146
|
- [adding a json column](#adding-a-json-column)
|
144
147
|
- [using primary key with short integer type](#using-primary-key-with-short-integer-type)
|
145
148
|
- [hash indexes](#hash-indexes)
|
@@ -321,6 +324,9 @@ A safer approach can be accomplished in several steps:
|
|
321
324
|
end
|
322
325
|
```
|
323
326
|
|
327
|
+
**Note**: `initialize_column_type_change` accepts additional options (like `:limit`, `:default` etc)
|
328
|
+
which will be passed to `add_column` when creating a new column, so you can override previous values.
|
329
|
+
|
324
330
|
2. Backfill data from the old column to the new column:
|
325
331
|
|
326
332
|
```ruby
|
@@ -395,7 +401,7 @@ For the previous example, to rename `name` column to `first_name` of the `users`
|
|
395
401
|
```sql
|
396
402
|
BEGIN;
|
397
403
|
ALTER TABLE users RENAME TO users_column_rename;
|
398
|
-
CREATE VIEW users AS SELECT *, first_name AS name FROM
|
404
|
+
CREATE VIEW users AS SELECT *, first_name AS name FROM users_column_rename;
|
399
405
|
COMMIT;
|
400
406
|
```
|
401
407
|
|
@@ -414,9 +420,21 @@ OnlineMigrations.config.column_renames = {
|
|
414
420
|
}
|
415
421
|
}
|
416
422
|
```
|
423
|
+
NOTE: You also need to temporarily enable partial writes (is disabled by default in Active Record >= 7)
|
424
|
+
until the process of column rename is fully done.
|
425
|
+
```ruby
|
426
|
+
# config/application.rb
|
427
|
+
# For Active Record >= 7
|
428
|
+
config.active_record.partial_inserts = true
|
429
|
+
|
430
|
+
# Or for Active Record < 7
|
431
|
+
config.active_record.partial_writes = true
|
432
|
+
```
|
417
433
|
|
418
434
|
2. Deploy
|
419
|
-
3.
|
435
|
+
3. Tell the database that you are going to rename a column. This will not actually rename any columns,
|
436
|
+
nor any data/indexes/foreign keys copying will be made, so will be instantaneous.
|
437
|
+
It will use a combination of a VIEW and column aliasing to work with both column names simultaneously
|
420
438
|
|
421
439
|
```ruby
|
422
440
|
class InitializeRenameUsersNameToFirstName < ActiveRecord::Migration[7.0]
|
@@ -429,7 +447,7 @@ end
|
|
429
447
|
4. Replace usages of the old column with a new column in the codebase
|
430
448
|
5. Deploy
|
431
449
|
6. Remove the column rename config from step 1
|
432
|
-
7. Remove the VIEW created in step 3:
|
450
|
+
7. Remove the VIEW created in step 3 and finally rename the column:
|
433
451
|
|
434
452
|
```ruby
|
435
453
|
class FinalizeRenameUsersNameToFirstName < ActiveRecord::Migration[7.0]
|
@@ -798,6 +816,24 @@ end
|
|
798
816
|
|
799
817
|
**Note**: If you forget `disable_ddl_transaction!`, the migration will fail.
|
800
818
|
|
819
|
+
### Adding an exclusion constraint
|
820
|
+
|
821
|
+
:x: **Bad**
|
822
|
+
|
823
|
+
Adding an exclusion constraint blocks reads and writes while every row is checked.
|
824
|
+
|
825
|
+
```ruby
|
826
|
+
class AddExclusionContraint < ActiveRecord::Migration[7.1]
|
827
|
+
def change
|
828
|
+
add_exclusion_constraint :users, "number WITH =", using: :gist
|
829
|
+
end
|
830
|
+
end
|
831
|
+
```
|
832
|
+
|
833
|
+
:white_check_mark: **Good**
|
834
|
+
|
835
|
+
[Let us know](https://github.com/fatkodima/online_migrations/issues/new) if you have a safe way to do this (exclusion constraints cannot be marked `NOT VALID`).
|
836
|
+
|
801
837
|
### Adding a json column
|
802
838
|
|
803
839
|
:x: **Bad**
|
@@ -107,19 +107,26 @@ module OnlineMigrations
|
|
107
107
|
transaction do
|
108
108
|
columns_and_types.each do |(column_name, new_type)|
|
109
109
|
old_col = __column_for(table_name, column_name)
|
110
|
+
old_col_options = __options_from_column(old_col, [:collation, :comment])
|
110
111
|
column_options = options[column_name] || {}
|
111
112
|
tmp_column_name = conversions[column_name]
|
112
113
|
|
113
|
-
if raw_connection.server_version >= 11_00_00
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
114
|
+
if raw_connection.server_version >= 11_00_00
|
115
|
+
if primary_key(table_name) == column_name.to_s && old_col.type == :integer
|
116
|
+
# If the column to be converted is a Primary Key, set it to
|
117
|
+
# `NOT NULL DEFAULT 0` and we'll copy the correct values when backfilling.
|
118
|
+
# That way, we skip the expensive validation step required to add
|
119
|
+
# a `NOT NULL` constraint at the end of the process.
|
120
|
+
add_column(table_name, tmp_column_name, new_type,
|
121
|
+
**old_col_options.merge(column_options).merge(default: old_col.default || 0, null: false))
|
122
|
+
else
|
123
|
+
unless old_col.default.nil?
|
124
|
+
old_col_options = old_col_options.merge(default: old_col.default, null: old_col.null)
|
125
|
+
end
|
126
|
+
add_column(table_name, tmp_column_name, new_type, **old_col_options.merge(column_options))
|
127
|
+
end
|
121
128
|
else
|
122
|
-
add_column(table_name, tmp_column_name, new_type, **column_options)
|
129
|
+
add_column(table_name, tmp_column_name, new_type, **old_col_options.merge(column_options))
|
123
130
|
change_column_default(table_name, tmp_column_name, old_col.default) unless old_col.default.nil?
|
124
131
|
end
|
125
132
|
end
|
@@ -377,6 +384,17 @@ module OnlineMigrations
|
|
377
384
|
"#{column_name}_for_type_change"
|
378
385
|
end
|
379
386
|
|
387
|
+
def __options_from_column(column, options)
|
388
|
+
result = {}
|
389
|
+
options.each do |option|
|
390
|
+
if column.respond_to?(option)
|
391
|
+
value = column.public_send(option)
|
392
|
+
result[option] = value unless value.nil?
|
393
|
+
end
|
394
|
+
end
|
395
|
+
result
|
396
|
+
end
|
397
|
+
|
380
398
|
def __copy_triggers_name(table_name, from_column, to_column)
|
381
399
|
CopyTrigger.on_table(table_name, connection: self).name(from_column, to_column)
|
382
400
|
end
|
@@ -229,6 +229,10 @@ module OnlineMigrations
|
|
229
229
|
|
230
230
|
type = type.to_sym
|
231
231
|
|
232
|
+
# Ignore internal Active Record migrations compatibility related
|
233
|
+
# options, like `_uses_legacy_table_name` etc. They all are starting with "_".
|
234
|
+
options = options.reject { |key, _| key.to_s.start_with?("_") }
|
235
|
+
|
232
236
|
existing_column = column_for(table_name, column_name)
|
233
237
|
if existing_column
|
234
238
|
existing_type = existing_column.type.to_sym
|
@@ -518,6 +522,12 @@ module OnlineMigrations
|
|
518
522
|
end
|
519
523
|
end
|
520
524
|
|
525
|
+
def add_exclusion_constraint(table_name, _expression, **_options)
|
526
|
+
unless new_or_small_table?(table_name)
|
527
|
+
raise_error :add_exclusion_constraint
|
528
|
+
end
|
529
|
+
end
|
530
|
+
|
521
531
|
def add_check_constraint(table_name, expression, **options)
|
522
532
|
if !new_or_small_table?(table_name) && options[:validate] != false
|
523
533
|
name = options[:name] || check_constraint_name(table_name, expression)
|
@@ -131,7 +131,8 @@ migration_helpers provides a safer approach to do this:
|
|
131
131
|
}
|
132
132
|
}
|
133
133
|
<% unless partial_writes %>
|
134
|
-
NOTE: You also need to temporarily enable partial writes
|
134
|
+
NOTE: You also need to temporarily enable partial writes (is disabled by default in Active Record >= 7)
|
135
|
+
until the process of column rename is fully done.
|
135
136
|
# config/application.rb
|
136
137
|
config.active_record.<%= partial_writes_setting %> = true
|
137
138
|
<% end %>
|
@@ -150,7 +151,7 @@ It will use a combination of a VIEW and column aliasing to work with both column
|
|
150
151
|
4. Replace usages of the old column with a new column in the codebase
|
151
152
|
5. Deploy
|
152
153
|
6. Remove the column rename config from step 1
|
153
|
-
7. Remove the VIEW created in step 3:
|
154
|
+
7. Remove the VIEW created in step 3 and finally rename the column:
|
154
155
|
|
155
156
|
class Finalize<%= migration_name %> < <%= migration_parent %>
|
156
157
|
def change
|
@@ -175,6 +176,9 @@ A safer approach can be accomplished in several steps:
|
|
175
176
|
end
|
176
177
|
end
|
177
178
|
|
179
|
+
**Note**: `initialize_column_type_change` accepts additional options (like `:limit`, `:default` etc)
|
180
|
+
which will be passed to `add_column` when creating a new column, so you can override previous values.
|
181
|
+
|
178
182
|
2. Backfill data from the old column to the new column:
|
179
183
|
|
180
184
|
class Backfill<%= migration_name %> < <%= migration_parent %>
|
@@ -380,6 +384,9 @@ end",
|
|
380
384
|
"Validating a foreign key while holding heavy locks on tables is dangerous.
|
381
385
|
Use disable_ddl_transaction! or a separate migration.",
|
382
386
|
|
387
|
+
add_exclusion_constraint:
|
388
|
+
"Adding an exclusion constraint blocks reads and writes while every row is checked.",
|
389
|
+
|
383
390
|
add_check_constraint:
|
384
391
|
"Adding a check constraint blocks reads and writes while every row is checked.
|
385
392
|
A safer approach is to add the check constraint without validating existing rows,
|
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.6.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-
|
11
|
+
date: 2023-02-04 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.
|
104
|
+
rubygems_version: 3.4.3
|
105
105
|
signing_key:
|
106
106
|
specification_version: 4
|
107
107
|
summary: Catch unsafe PostgreSQL migrations in development and run them easier in
|