online_migrations 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/BACKGROUND_MIGRATIONS.md +6 -0
- data/CHANGELOG.md +44 -0
- data/README.md +45 -6
- data/lib/online_migrations/background_migrations/backfill_column.rb +3 -3
- data/lib/online_migrations/background_migrations/copy_column.rb +2 -1
- data/lib/online_migrations/background_migrations/migration.rb +3 -2
- data/lib/online_migrations/background_migrations/migration_helpers.rb +39 -0
- data/lib/online_migrations/background_migrations/reset_counters.rb +101 -0
- data/lib/online_migrations/change_column_type_helpers.rb +1 -1
- data/lib/online_migrations/command_checker.rb +72 -13
- data/lib/online_migrations/error_messages.rb +4 -2
- data/lib/online_migrations/schema_statements.rb +12 -3
- data/lib/online_migrations/utils.rb +9 -2
- data/lib/online_migrations/version.rb +1 -1
- data/lib/online_migrations.rb +49 -34
- metadata +3 -18
- data/.github/workflows/test.yml +0 -112
- data/.gitignore +0 -11
- data/.rubocop.yml +0 -120
- data/.yardopts +0 -1
- data/Gemfile +0 -27
- data/Gemfile.lock +0 -108
- data/Rakefile +0 -23
- data/gemfiles/activerecord_42.gemfile +0 -6
- data/gemfiles/activerecord_50.gemfile +0 -5
- data/gemfiles/activerecord_51.gemfile +0 -5
- data/gemfiles/activerecord_52.gemfile +0 -5
- data/gemfiles/activerecord_60.gemfile +0 -5
- data/gemfiles/activerecord_61.gemfile +0 -5
- data/gemfiles/activerecord_70.gemfile +0 -5
- data/gemfiles/activerecord_head.gemfile +0 -5
- data/online_migrations.gemspec +0 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 87bf9f8f917c2d14e8a2e5584a2710dec5ccd66051730b8bb1b5a0b765b70ccb
|
4
|
+
data.tar.gz: 0cbf86b34c7d62f1466c877df231c2d39f3f4d5c2bd4f4dbef080f56e3254a74
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3184f370a4cb3a3fa150ff24644355bc6c928eaa88331a0e18ee6f2c2448917898f5165675381162701f49881018516533b97e2364204a5a6ebd8bb19b3063cc
|
7
|
+
data.tar.gz: 954f9d33eba0e94020e0c9de77fd47097cd192915ad60a6c08aff8ab6d7a54b06a6938a50191c6b75596ae06b427076809472098b90a633549f2208ca358c656
|
data/BACKGROUND_MIGRATIONS.md
CHANGED
@@ -114,6 +114,12 @@ enqueue_background_migration("MyMigrationWithArgs", arg1, arg2, ...)
|
|
114
114
|
* **Isolation**: Background migrations should be isolated and not use application code (for example, models defined in `app/models`). Since these migrations can take a long time to run it's possible for new versions to be deployed while they are still running.
|
115
115
|
* **Idempotence**: It should be safe to run `process_batch` multiple times for the same elements. It's important if the Background Migration errors and you run it again, because the same element that errored may be processed again. Make sure that in case that your migration job is going to be retried data integrity is guaranteed.
|
116
116
|
|
117
|
+
## Predefined background migrations
|
118
|
+
|
119
|
+
* `BackfillColumn` - backfills column(s) with scalar values
|
120
|
+
* `CopyColumn` - copies data from one column(s) to other(s)
|
121
|
+
* `ResetCounters` - resets one or more counter caches to their correct value
|
122
|
+
|
117
123
|
## Testing
|
118
124
|
|
119
125
|
At a minimum, it's recommended that the `#process_batch` method in your background migration is tested. You may also want to test the `#relation` and `#count` methods if they are sufficiently complex.
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,49 @@
|
|
1
1
|
## master (unreleased)
|
2
2
|
|
3
|
+
- Lazy load this gem
|
4
|
+
|
5
|
+
- Add ability to reset counter caches using background migrations
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
class User < ApplicationRecord
|
9
|
+
has_many :projects
|
10
|
+
end
|
11
|
+
|
12
|
+
class Project < ApplicationRecord
|
13
|
+
belongs_to :user, counter_cache: true
|
14
|
+
end
|
15
|
+
|
16
|
+
class ResetUsersProjectsCount < ActiveRecord::Migration[7.0]
|
17
|
+
def up
|
18
|
+
reset_counters_in_background("User", :projects)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
```
|
22
|
+
|
23
|
+
- Accept `0` as `batch_pause` value for background migrations
|
24
|
+
- Ignore default scopes in `CopyColumn` and `BackfillColumn` background migrations
|
25
|
+
- Raise an error for unsupported database versions
|
26
|
+
- Fix backfilling code in suggestion for changing column's NOT NULL
|
27
|
+
|
28
|
+
New safe operations
|
29
|
+
|
30
|
+
- Changing between `text` and `citext` when not indexed
|
31
|
+
- Changing a `string` column to a `citext` column when not indexed
|
32
|
+
- Changing a `citext` column to a `string` column with no length limit
|
33
|
+
- Increasing the `:precision` of an `interval` column
|
34
|
+
- Changing a `cidr` column to an `inet` column
|
35
|
+
- Changing an `xml` column to a `text` column
|
36
|
+
- Changing an `xml` column to a `string` column with no `:limit`
|
37
|
+
- Changing a `bit` column to a `bit_varying` column
|
38
|
+
- Increasing or removing the `:limit` of a `bit_varying` column
|
39
|
+
|
40
|
+
New unsafe operations
|
41
|
+
|
42
|
+
- Decreasing `:precision` of a `datetime` column
|
43
|
+
- Decreasing `:limit` of a `timestamptz` column
|
44
|
+
- Decreasing `:limit` of a `bit_varying` column
|
45
|
+
- Adding a `:limit` to a `bit_varying` column
|
46
|
+
|
3
47
|
## 0.3.0 (2022-02-10)
|
4
48
|
|
5
49
|
- Support ActiveRecord 7.0+ versioned schemas
|
data/README.md
CHANGED
@@ -10,6 +10,10 @@ Catch unsafe PostgreSQL migrations in development and run them easier in product
|
|
10
10
|
|
11
11
|
[![Build Status](https://github.com/fatkodima/online_migrations/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/fatkodima/online_migrations/actions/workflows/test.yml)
|
12
12
|
|
13
|
+
## Cool, but there is a `strong_migrations` already
|
14
|
+
|
15
|
+
See [comparison to `strong_migrations`](#comparison-to-strong_migrations)
|
16
|
+
|
13
17
|
## Requirements
|
14
18
|
|
15
19
|
- Ruby 2.1+
|
@@ -282,12 +286,20 @@ end
|
|
282
286
|
|
283
287
|
A few changes don't require a table rewrite (and are safe) in PostgreSQL:
|
284
288
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
289
|
+
Type | Safe Changes
|
290
|
+
--- | ---
|
291
|
+
`bit` | Changing to `bit_varying`
|
292
|
+
`bit_varying` | Increasing or removing `:limit`
|
293
|
+
`cidr` | Changing to `inet`
|
294
|
+
`citext` | Changing to `text` if not indexed, changing to `string` with no `:limit` if not indexed
|
295
|
+
`datetime` | Increasing or removing `:precision`, changing to `timestamptz` when session time zone is UTC in PostgreSQL 12+
|
296
|
+
`decimal` | Increasing `:precision` at same `:scale`, removing `:precision` and `:scale`
|
297
|
+
`interval` | Increasing or removing `:precision`
|
298
|
+
`numeric` | Increasing `:precision` at same `:scale`, removing `:precision` and `:scale`
|
299
|
+
`string` | Increasing or removing `:limit`, changing to `text`, changing to `citext` if not indexed
|
300
|
+
`text` | Changing to `string` with no `:limit`, changing to `citext` if not indexed
|
301
|
+
`timestamptz` | Increasing or removing `:limit`, changing to `datetime` when session time zone is UTC in PostgreSQL 12+
|
302
|
+
`xml` | Changing to `text`, changing to `string` with no `:limit`
|
291
303
|
|
292
304
|
:white_check_mark: **Good**
|
293
305
|
|
@@ -1292,6 +1304,33 @@ Background migrations:
|
|
1292
1304
|
- add UI
|
1293
1305
|
- support batching over non-integer and multiple columns
|
1294
1306
|
|
1307
|
+
## Comparison to `strong_migrations`
|
1308
|
+
|
1309
|
+
This gem was heavily inspired by the `strong_migrations` and GitLab's approaches to database migrations. This gem is a superset of `strong_migrations`, feature-wise, and has the same APIs.
|
1310
|
+
|
1311
|
+
The main differences are:
|
1312
|
+
|
1313
|
+
1. `strong_migrations` provides you **text guidance** on how to run migrations safer and you should implement them yourself. This new gem has actual [**code helpers**](https://github.com/fatkodima/online_migrations/blob/master/lib/online_migrations/schema_statements.rb) (and suggests them when fails on unsafe migrations) you can use to do what you want. See [example](#example) for an example.
|
1314
|
+
|
1315
|
+
It has migrations helpers for:
|
1316
|
+
|
1317
|
+
* renaming tables/columns
|
1318
|
+
* changing columns types (including changing primary/foreign keys from `integer` to `bigint`)
|
1319
|
+
* adding columns with default values
|
1320
|
+
* backfilling data
|
1321
|
+
* adding different types of constraints
|
1322
|
+
* and others
|
1323
|
+
|
1324
|
+
2. This gem has a [powerful internal framework](https://github.com/fatkodima/online_migrations/blob/master/BACKGROUND_MIGRATIONS.md) for running data migrations on very large tables using background migrations.
|
1325
|
+
|
1326
|
+
For example, you can use background migrations to migrate data that’s stored in a single JSON column to a separate table instead; backfill values from one column to another (as one of the steps when changing column type); or backfill some column’s value from an API.
|
1327
|
+
|
1328
|
+
3. Yet, it has more checks for unsafe changes (see [checks](#checks)).
|
1329
|
+
|
1330
|
+
4. Currently, this gem supports only PostgreSQL, while `strong_migrations` also checks `MySQL` and `MariaDB` migrations.
|
1331
|
+
|
1332
|
+
5. This gem is more flexible in terms of configuration - see [config file](https://github.com/fatkodima/online_migrations/blob/master/lib/online_migrations/config.rb) for additional configuration options.
|
1333
|
+
|
1295
1334
|
## License
|
1296
1335
|
|
1297
1336
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -20,9 +20,9 @@ module OnlineMigrations
|
|
20
20
|
# Otherwise, the SQL is `WHERE column != value`. This condition ignores column
|
21
21
|
# with NULLs in it, so we need to also manually check for NULLs.
|
22
22
|
quoted_column = connection.quote_column_name(column)
|
23
|
-
model.where("#{quoted_column} != ? OR #{quoted_column} IS NULL", value)
|
23
|
+
model.unscoped.where("#{quoted_column} != ? OR #{quoted_column} IS NULL", value)
|
24
24
|
else
|
25
|
-
Utils.ar_where_not_multiple_conditions(model, updates)
|
25
|
+
Utils.ar_where_not_multiple_conditions(model.unscoped, updates)
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
@@ -42,7 +42,7 @@ module OnlineMigrations
|
|
42
42
|
@model ||= if model_name.present?
|
43
43
|
Object.const_get(model_name, false)
|
44
44
|
else
|
45
|
-
Utils.define_model(
|
45
|
+
Utils.define_model(table_name)
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
@@ -26,6 +26,7 @@ module OnlineMigrations
|
|
26
26
|
|
27
27
|
def relation
|
28
28
|
relation = model
|
29
|
+
.unscoped
|
29
30
|
.where(copy_to.map { |to_column| [to_column, nil] }.to_h)
|
30
31
|
|
31
32
|
Utils.ar_where_not_multiple_conditions(
|
@@ -78,7 +79,7 @@ module OnlineMigrations
|
|
78
79
|
@model ||= if model_name.present?
|
79
80
|
Object.const_get(model_name, false)
|
80
81
|
else
|
81
|
-
Utils.define_model(
|
82
|
+
Utils.define_model(table_name)
|
82
83
|
end
|
83
84
|
end
|
84
85
|
|
@@ -27,10 +27,11 @@ module OnlineMigrations
|
|
27
27
|
|
28
28
|
validates :migration_name, :batch_column_name, presence: true
|
29
29
|
|
30
|
-
validates :
|
30
|
+
validates :min_value, :max_value, :batch_size, :sub_batch_size,
|
31
31
|
presence: true, numericality: { greater_than: 0 }
|
32
32
|
|
33
|
-
validates :sub_batch_pause_ms, presence: true,
|
33
|
+
validates :batch_pause, :sub_batch_pause_ms, presence: true,
|
34
|
+
numericality: { greater_than_or_equal_to: 0 }
|
34
35
|
validates :rows_count, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
|
35
36
|
validates :arguments, uniqueness: { scope: :migration_name }
|
36
37
|
|
@@ -166,6 +166,45 @@ module OnlineMigrations
|
|
166
166
|
)
|
167
167
|
end
|
168
168
|
|
169
|
+
# Resets one or more counter caches to their correct value using background migrations.
|
170
|
+
# This is useful when adding new counter caches, or if the counter has been corrupted or modified directly by SQL.
|
171
|
+
#
|
172
|
+
# @param model_name [String]
|
173
|
+
# @param counters [Array]
|
174
|
+
# @param touch [Boolean, Symbol, Array] touch timestamp columns when updating.
|
175
|
+
# - when `true` - will touch `updated_at` and/or `updated_on`
|
176
|
+
# - when `Symbol` or `Array` - will touch specific column(s)
|
177
|
+
# @param options [Hash] used to control the behavior of background migration.
|
178
|
+
# See `#enqueue_background_migration`
|
179
|
+
#
|
180
|
+
# @return [OnlineMigrations::BackgroundMigrations::Migration]
|
181
|
+
#
|
182
|
+
# @example
|
183
|
+
# reset_counters_in_background("User", :projects, :friends, touch: true)
|
184
|
+
#
|
185
|
+
# @example Touch specific column
|
186
|
+
# reset_counters_in_background("User", :projects, touch: :touched_at)
|
187
|
+
#
|
188
|
+
# @example Touch with specific time value
|
189
|
+
# reset_counters_in_background("User", :projects, touch: [time: 2.days.ago])
|
190
|
+
#
|
191
|
+
# @see https://api.rubyonrails.org/classes/ActiveRecord/CounterCache/ClassMethods.html#method-i-reset_counters
|
192
|
+
#
|
193
|
+
# @note This method is better suited for extra large tables (100s of millions of records).
|
194
|
+
# For smaller tables it is probably better and easier to use `reset_counters` from the ActiveRecord.
|
195
|
+
#
|
196
|
+
def reset_counters_in_background(model_name, *counters, touch: nil, **options)
|
197
|
+
model_name = model_name.name if model_name.is_a?(Class)
|
198
|
+
|
199
|
+
enqueue_background_migration(
|
200
|
+
"ResetCounters",
|
201
|
+
model_name,
|
202
|
+
counters,
|
203
|
+
{ touch: touch },
|
204
|
+
**options
|
205
|
+
)
|
206
|
+
end
|
207
|
+
|
169
208
|
# Creates a background migration for the given job class name.
|
170
209
|
#
|
171
210
|
# A background migration runs one job at a time, computing the bounds of the next batch
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OnlineMigrations
|
4
|
+
module BackgroundMigrations
|
5
|
+
# @private
|
6
|
+
class ResetCounters < BackgroundMigration
|
7
|
+
attr_reader :model, :counters, :touch
|
8
|
+
|
9
|
+
def initialize(model_name, counters, options = {})
|
10
|
+
@model = Object.const_get(model_name, false)
|
11
|
+
@counters = counters
|
12
|
+
@touch = options[:touch]
|
13
|
+
end
|
14
|
+
|
15
|
+
def relation
|
16
|
+
model.unscoped
|
17
|
+
end
|
18
|
+
|
19
|
+
def process_batch(relation)
|
20
|
+
updates = counters.map do |counter_association|
|
21
|
+
has_many_association = has_many_association(counter_association)
|
22
|
+
|
23
|
+
foreign_key = has_many_association.foreign_key.to_s
|
24
|
+
child_class = has_many_association.klass
|
25
|
+
reflection = child_class._reflections.values.find { |e| e.belongs_to? && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
|
26
|
+
counter_name = reflection.counter_cache_column
|
27
|
+
|
28
|
+
quoted_association_table = connection.quote_table_name(has_many_association.table_name)
|
29
|
+
count_subquery = <<-SQL.strip_heredoc
|
30
|
+
SELECT COUNT(*)
|
31
|
+
FROM #{quoted_association_table}
|
32
|
+
WHERE #{quoted_association_table}.#{connection.quote_column_name(foreign_key)} =
|
33
|
+
#{model.quoted_table_name}.#{model.quoted_primary_key}
|
34
|
+
SQL
|
35
|
+
|
36
|
+
"#{connection.quote_column_name(counter_name)} = (#{count_subquery})"
|
37
|
+
end
|
38
|
+
|
39
|
+
if touch
|
40
|
+
names = touch if touch != true
|
41
|
+
names = Array.wrap(names)
|
42
|
+
options = names.extract_options!
|
43
|
+
touch_updates = touch_attributes_with_time(*names, **options)
|
44
|
+
# In ActiveRecord 4.2 sanitize_sql_for_assignment is protected
|
45
|
+
updates << model.send(:sanitize_sql_for_assignment, touch_updates)
|
46
|
+
end
|
47
|
+
|
48
|
+
relation.update_all(updates.join(", "))
|
49
|
+
end
|
50
|
+
|
51
|
+
def count
|
52
|
+
# Exact counts are expensive on large tables, since PostgreSQL
|
53
|
+
# needs to do a full scan. An estimated count should give a pretty decent
|
54
|
+
# approximation of rows count in this case.
|
55
|
+
Utils.estimated_count(connection, model.table_name)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
def has_many_association(counter_association) # rubocop:disable Naming/PredicateName
|
60
|
+
has_many_association = model.reflect_on_association(counter_association)
|
61
|
+
|
62
|
+
unless has_many_association
|
63
|
+
has_many = model.reflect_on_all_associations(:has_many)
|
64
|
+
|
65
|
+
has_many_association = has_many.find do |association|
|
66
|
+
counter_cache_column = association.counter_cache_column
|
67
|
+
|
68
|
+
# ActiveRecord <= 4.2 is able to return only explicitly provided `counter_cache` column.
|
69
|
+
if !counter_cache_column && Utils.ar_version <= 4.2
|
70
|
+
counter_cache_column = "#{association.name}_count"
|
71
|
+
end
|
72
|
+
counter_cache_column && counter_cache_column.to_sym == counter_association.to_sym
|
73
|
+
end
|
74
|
+
|
75
|
+
counter_association = has_many_association.plural_name if has_many_association
|
76
|
+
end
|
77
|
+
raise ArgumentError, "'#{model.name}' has no association called '#{counter_association}'" unless has_many_association
|
78
|
+
|
79
|
+
if has_many_association.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
80
|
+
has_many_association = has_many_association.through_reflection
|
81
|
+
end
|
82
|
+
|
83
|
+
has_many_association
|
84
|
+
end
|
85
|
+
|
86
|
+
def touch_attributes_with_time(*names, time: nil)
|
87
|
+
attribute_names = timestamp_attributes_for_update & model.column_names
|
88
|
+
attribute_names |= names.map(&:to_s)
|
89
|
+
attribute_names.map { |attribute_name| [attribute_name, time || Time.current] }.to_h
|
90
|
+
end
|
91
|
+
|
92
|
+
def timestamp_attributes_for_update
|
93
|
+
["updated_at", "updated_on"].map { |name| model.attribute_aliases[name] || name }
|
94
|
+
end
|
95
|
+
|
96
|
+
def connection
|
97
|
+
model.connection
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -110,7 +110,7 @@ module OnlineMigrations
|
|
110
110
|
column_options = options[column_name] || {}
|
111
111
|
tmp_column_name = conversions[column_name]
|
112
112
|
|
113
|
-
if
|
113
|
+
if __raw_connection.server_version >= 11_00_00 &&
|
114
114
|
primary_key(table_name) == column_name.to_s && old_col.type == :integer
|
115
115
|
# If the column to be converted is a Primary Key, set it to
|
116
116
|
# `NOT NULL DEFAULT 0` and we'll copy the correct values when backfilling.
|
@@ -27,6 +27,7 @@ module OnlineMigrations
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def check(command, *args, &block)
|
30
|
+
check_database_version
|
30
31
|
check_lock_timeout
|
31
32
|
|
32
33
|
unless safe?
|
@@ -43,6 +44,22 @@ module OnlineMigrations
|
|
43
44
|
end
|
44
45
|
|
45
46
|
private
|
47
|
+
def check_database_version
|
48
|
+
return if defined?(@database_version_checked)
|
49
|
+
|
50
|
+
adapter = connection.adapter_name
|
51
|
+
case adapter
|
52
|
+
when /postg/i
|
53
|
+
if postgresql_version < Gem::Version.new("9.6")
|
54
|
+
raise "#{adapter} < 9.6 is not supported"
|
55
|
+
end
|
56
|
+
else
|
57
|
+
raise "#{adapter} is not supported"
|
58
|
+
end
|
59
|
+
|
60
|
+
@database_version_checked = true
|
61
|
+
end
|
62
|
+
|
46
63
|
def check_lock_timeout
|
47
64
|
limit = OnlineMigrations.config.lock_timeout_limit
|
48
65
|
|
@@ -206,10 +223,21 @@ module OnlineMigrations
|
|
206
223
|
|
207
224
|
type = type.to_sym
|
208
225
|
|
209
|
-
existing_column =
|
226
|
+
existing_column = column_for(table_name, column_name)
|
210
227
|
if existing_column
|
211
228
|
existing_type = existing_column.type.to_sym
|
212
229
|
|
230
|
+
# To get a list of binary-coercible types:
|
231
|
+
#
|
232
|
+
# SELECT stype.typname AS source, ttype.typname AS target
|
233
|
+
# FROM pg_cast
|
234
|
+
# INNER JOIN pg_type stype ON pg_cast.castsource = stype.oid
|
235
|
+
# INNER JOIN pg_type ttype ON pg_cast.casttarget = ttype.oid
|
236
|
+
# WHERE castmethod = 'b'
|
237
|
+
# ORDER BY 1, 2
|
238
|
+
|
239
|
+
# https://www.postgresql.org/docs/release/9.2.0/#AEN124164
|
240
|
+
|
213
241
|
safe =
|
214
242
|
case type
|
215
243
|
when :string
|
@@ -218,12 +246,25 @@ module OnlineMigrations
|
|
218
246
|
case existing_type
|
219
247
|
when :string
|
220
248
|
!options[:limit] || (existing_column.limit && options[:limit] >= existing_column.limit)
|
221
|
-
when :text
|
249
|
+
when :text, :xml
|
222
250
|
!options[:limit]
|
251
|
+
when :citext
|
252
|
+
!options[:limit] && !indexed?(table_name, column_name)
|
223
253
|
end
|
224
254
|
when :text
|
225
|
-
|
226
|
-
|
255
|
+
[:string, :text, :xml].include?(existing_type) ||
|
256
|
+
(existing_type == :citext && !indexed?(table_name, column_name))
|
257
|
+
when :citext
|
258
|
+
[:string, :text].include?(existing_type) && !indexed?(table_name, column_name)
|
259
|
+
when :bit_varying
|
260
|
+
case existing_type
|
261
|
+
when :bit
|
262
|
+
!options[:limit]
|
263
|
+
when :bit_varying
|
264
|
+
# safe to increase limit or remove it
|
265
|
+
# not safe to decrease limit or add a limit
|
266
|
+
!options[:limit] || (existing_column.limit && options[:limit] >= existing_column.limit)
|
267
|
+
end
|
227
268
|
when :numeric, :decimal
|
228
269
|
# numeric and decimal are equivalent and can be used interchangably
|
229
270
|
[:numeric, :decimal].include?(existing_type) &&
|
@@ -239,9 +280,25 @@ module OnlineMigrations
|
|
239
280
|
)
|
240
281
|
)
|
241
282
|
when :datetime, :timestamp, :timestamptz
|
242
|
-
|
243
|
-
|
244
|
-
|
283
|
+
# precision for datetime
|
284
|
+
# limit for timestamp, timestamptz
|
285
|
+
precision = (type == :datetime ? options[:precision] : options[:limit]) || 6
|
286
|
+
existing_precision = existing_column.precision || existing_column.limit || 6
|
287
|
+
|
288
|
+
[:datetime, :timestamp, :timestamptz].include?(existing_type) &&
|
289
|
+
precision >= existing_precision &&
|
290
|
+
(type == existing_type ||
|
291
|
+
(postgresql_version >= Gem::Version.new("12") &&
|
292
|
+
connection.select_value("SHOW timezone") == "UTC"))
|
293
|
+
when :interval
|
294
|
+
precision = options[:precision] || options[:limit] || 6
|
295
|
+
existing_precision = existing_column.precision || existing_column.limit || 6
|
296
|
+
|
297
|
+
# PostgreSQL interval data type was added in https://github.com/rails/rails/pull/16919
|
298
|
+
(existing_type == :interval || (Utils.ar_version < 6.1 && existing_column.sql_type.start_with?("interval"))) &&
|
299
|
+
precision >= existing_precision
|
300
|
+
when :inet
|
301
|
+
existing_type == :cidr
|
245
302
|
else
|
246
303
|
type == existing_type &&
|
247
304
|
options[:limit] == existing_column.limit &&
|
@@ -281,15 +338,13 @@ module OnlineMigrations
|
|
281
338
|
constraint_name = "#{table_name}_#{column_name}_null"
|
282
339
|
vars = {
|
283
340
|
add_constraint_code: command_str(:add_not_null_constraint, table_name, column_name, name: constraint_name, validate: false),
|
284
|
-
backfill_code: nil,
|
285
341
|
validate_constraint_code: command_str(:validate_not_null_constraint, table_name, column_name, name: constraint_name),
|
286
342
|
remove_constraint_code: nil,
|
343
|
+
table_name: table_name,
|
344
|
+
column_name: column_name,
|
345
|
+
default: default,
|
287
346
|
}
|
288
347
|
|
289
|
-
if !default.nil?
|
290
|
-
vars[:backfill_code] = command_str(:update_column_in_batches, table_name, column_name, default)
|
291
|
-
end
|
292
|
-
|
293
348
|
if postgresql_version >= Gem::Version.new("12")
|
294
349
|
vars[:remove_constraint_code] = command_str(:remove_check_constraint, table_name, name: constraint_name)
|
295
350
|
vars[:change_column_null_code] = command_str(:change_column_null, table_name, column_name, false)
|
@@ -537,7 +592,7 @@ module OnlineMigrations
|
|
537
592
|
target_version.to_s
|
538
593
|
else
|
539
594
|
# For rails 6.0+ we can use connection.database_version
|
540
|
-
pg_connection = connection.
|
595
|
+
pg_connection = connection.send(:__raw_connection)
|
541
596
|
database_version = pg_connection.server_version
|
542
597
|
patch = database_version % 100
|
543
598
|
database_version /= 100
|
@@ -669,6 +724,10 @@ module OnlineMigrations
|
|
669
724
|
[:integer, :bigint, :serial, :bigserial, :uuid].include?(type)
|
670
725
|
end
|
671
726
|
|
727
|
+
def indexed?(table_name, column_name)
|
728
|
+
connection.indexes(table_name).any? { |index| index.columns.include?(column_name.to_s) }
|
729
|
+
end
|
730
|
+
|
672
731
|
def column_for(table_name, column_name)
|
673
732
|
connection.columns(table_name).find { |column| column.name == column_name.to_s }
|
674
733
|
end
|
@@ -214,11 +214,13 @@ class <%= migration_name %> < <%= migration_parent %>
|
|
214
214
|
|
215
215
|
def change
|
216
216
|
<%= add_constraint_code %>
|
217
|
-
<%
|
217
|
+
<% unless default.nil? %>
|
218
218
|
|
219
219
|
# Passing a default value to change_column_null runs a single UPDATE query,
|
220
220
|
# which can cause downtime. Instead, backfill the existing rows in batches.
|
221
|
-
<%=
|
221
|
+
update_column_in_batches(:<%= table_name %>, :<%= column_name %>, <%= default.inspect %>) do |relation|
|
222
|
+
relation.where(<%= column_name %>: nil)
|
223
|
+
end
|
222
224
|
|
223
225
|
<% end %>
|
224
226
|
<%= validate_constraint_code %>
|
@@ -81,7 +81,7 @@ module OnlineMigrations
|
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
84
|
-
model = Utils.define_model(
|
84
|
+
model = Utils.define_model(table_name, self)
|
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)
|
@@ -396,7 +396,7 @@ module OnlineMigrations
|
|
396
396
|
raise ArgumentError, "Expressions as default are not supported"
|
397
397
|
end
|
398
398
|
|
399
|
-
if
|
399
|
+
if __raw_connection.server_version >= 11_00_00 && !Utils.volatile_default?(self, type, default)
|
400
400
|
add_column(table_name, column_name, type, **options)
|
401
401
|
else
|
402
402
|
__ensure_not_in_transaction!
|
@@ -422,7 +422,7 @@ module OnlineMigrations
|
|
422
422
|
add_not_null_constraint(table_name, column_name, validate: false)
|
423
423
|
validate_not_null_constraint(table_name, column_name)
|
424
424
|
|
425
|
-
if
|
425
|
+
if __raw_connection.server_version >= 12_00_00
|
426
426
|
# In PostgreSQL 12+ it is safe to "promote" a CHECK constraint to `NOT NULL` for the column
|
427
427
|
change_column_null(table_name, column_name, false)
|
428
428
|
remove_not_null_constraint(table_name, column_name)
|
@@ -1046,5 +1046,14 @@ module OnlineMigrations
|
|
1046
1046
|
_, schema = table_name.to_s.split(".").reverse
|
1047
1047
|
schema ? quote(schema) : "current_schema()"
|
1048
1048
|
end
|
1049
|
+
|
1050
|
+
def __raw_connection
|
1051
|
+
# ActiveRecord > 7.0.2.2 (https://github.com/rails/rails/pull/44530)
|
1052
|
+
if defined?(@raw_connection)
|
1053
|
+
@raw_connection
|
1054
|
+
else
|
1055
|
+
@connection
|
1056
|
+
end
|
1057
|
+
end
|
1049
1058
|
end
|
1050
1059
|
end
|
@@ -53,7 +53,7 @@ module OnlineMigrations
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
-
def define_model(
|
56
|
+
def define_model(table_name, connection = ActiveRecord::Base.connection)
|
57
57
|
Class.new(ActiveRecord::Base) do
|
58
58
|
self.table_name = table_name
|
59
59
|
self.inheritance_column = :_type_disabled
|
@@ -101,7 +101,14 @@ module OnlineMigrations
|
|
101
101
|
WHERE relname = #{quoted_table}
|
102
102
|
AND relnamespace = current_schema()::regnamespace
|
103
103
|
SQL
|
104
|
-
|
104
|
+
|
105
|
+
if count
|
106
|
+
count = count.to_i
|
107
|
+
# If the table has never yet been vacuumed or analyzed, reltuples contains -1
|
108
|
+
# indicating that the row count is unknown.
|
109
|
+
count = 0 if count == -1
|
110
|
+
count
|
111
|
+
end
|
105
112
|
end
|
106
113
|
|
107
114
|
def ar_where_not_multiple_conditions(relation, conditions)
|
data/lib/online_migrations.rb
CHANGED
@@ -1,46 +1,61 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_record"
|
4
|
-
|
5
|
-
require "online_migrations/utils"
|
6
|
-
require "online_migrations/error_messages"
|
7
|
-
require "online_migrations/config"
|
8
|
-
require "online_migrations/batch_iterator"
|
9
|
-
require "online_migrations/verbose_sql_logs"
|
10
|
-
require "online_migrations/migration"
|
11
|
-
require "online_migrations/migrator"
|
12
|
-
require "online_migrations/database_tasks"
|
13
|
-
require "online_migrations/foreign_key_definition"
|
14
|
-
require "online_migrations/foreign_keys_collector"
|
15
|
-
require "online_migrations/index_definition"
|
16
|
-
require "online_migrations/indexes_collector"
|
17
|
-
require "online_migrations/command_checker"
|
18
|
-
require "online_migrations/schema_cache"
|
19
|
-
require "online_migrations/background_migration"
|
20
|
-
require "online_migrations/background_migrations/config"
|
21
|
-
require "online_migrations/background_migrations/migration_status_validator"
|
22
|
-
require "online_migrations/background_migrations/migration_job_status_validator"
|
23
|
-
require "online_migrations/background_migrations/background_migration_class_validator"
|
24
|
-
require "online_migrations/background_migrations/backfill_column"
|
25
|
-
require "online_migrations/background_migrations/copy_column"
|
26
|
-
require "online_migrations/background_migrations/migration_job"
|
27
|
-
require "online_migrations/background_migrations/migration"
|
28
|
-
require "online_migrations/background_migrations/migration_job_runner"
|
29
|
-
require "online_migrations/background_migrations/migration_runner"
|
30
|
-
require "online_migrations/background_migrations/migration_helpers"
|
31
|
-
require "online_migrations/background_migrations/advisory_lock"
|
32
|
-
require "online_migrations/background_migrations/scheduler"
|
33
|
-
require "online_migrations/lock_retrier"
|
34
|
-
require "online_migrations/command_recorder"
|
35
|
-
require "online_migrations/copy_trigger"
|
36
|
-
require "online_migrations/change_column_type_helpers"
|
37
|
-
require "online_migrations/schema_statements"
|
38
4
|
require "online_migrations/version"
|
39
5
|
|
40
6
|
module OnlineMigrations
|
41
7
|
class Error < StandardError; end
|
42
8
|
class UnsafeMigration < Error; end
|
43
9
|
|
10
|
+
extend ActiveSupport::Autoload
|
11
|
+
|
12
|
+
autoload :Utils
|
13
|
+
autoload :ErrorMessages
|
14
|
+
autoload :Config
|
15
|
+
autoload :BatchIterator
|
16
|
+
autoload :VerboseSqlLogs
|
17
|
+
autoload :Migration
|
18
|
+
autoload :Migrator
|
19
|
+
autoload :DatabaseTasks
|
20
|
+
autoload :ForeignKeyDefinition
|
21
|
+
autoload :ForeignKeysCollector
|
22
|
+
autoload :IndexDefinition
|
23
|
+
autoload :IndexesCollector
|
24
|
+
autoload :CommandChecker
|
25
|
+
autoload :SchemaCache
|
26
|
+
autoload :BackgroundMigration
|
27
|
+
|
28
|
+
autoload_at "online_migrations/lock_retrier" do
|
29
|
+
autoload :LockRetrier
|
30
|
+
autoload :ConstantLockRetrier
|
31
|
+
autoload :ExponentialLockRetrier
|
32
|
+
autoload :NullLockRetrier
|
33
|
+
end
|
34
|
+
|
35
|
+
autoload :CommandRecorder
|
36
|
+
autoload :CopyTrigger
|
37
|
+
autoload :ChangeColumnTypeHelpers
|
38
|
+
autoload :SchemaStatements
|
39
|
+
|
40
|
+
module BackgroundMigrations
|
41
|
+
extend ActiveSupport::Autoload
|
42
|
+
|
43
|
+
autoload :Config
|
44
|
+
autoload :MigrationStatusValidator
|
45
|
+
autoload :MigrationJobStatusValidator
|
46
|
+
autoload :BackgroundMigrationClassValidator
|
47
|
+
autoload :BackfillColumn
|
48
|
+
autoload :CopyColumn
|
49
|
+
autoload :ResetCounters
|
50
|
+
autoload :MigrationJob
|
51
|
+
autoload :Migration
|
52
|
+
autoload :MigrationJobRunner
|
53
|
+
autoload :MigrationRunner
|
54
|
+
autoload :MigrationHelpers
|
55
|
+
autoload :AdvisoryLock
|
56
|
+
autoload :Scheduler
|
57
|
+
end
|
58
|
+
|
44
59
|
class << self
|
45
60
|
# @private
|
46
61
|
attr_accessor :current_migration
|
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.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- fatkodima
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-03-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -31,25 +31,10 @@ executables: []
|
|
31
31
|
extensions: []
|
32
32
|
extra_rdoc_files: []
|
33
33
|
files:
|
34
|
-
- ".github/workflows/test.yml"
|
35
|
-
- ".gitignore"
|
36
|
-
- ".rubocop.yml"
|
37
|
-
- ".yardopts"
|
38
34
|
- BACKGROUND_MIGRATIONS.md
|
39
35
|
- CHANGELOG.md
|
40
|
-
- Gemfile
|
41
|
-
- Gemfile.lock
|
42
36
|
- LICENSE.txt
|
43
37
|
- README.md
|
44
|
-
- Rakefile
|
45
|
-
- gemfiles/activerecord_42.gemfile
|
46
|
-
- gemfiles/activerecord_50.gemfile
|
47
|
-
- gemfiles/activerecord_51.gemfile
|
48
|
-
- gemfiles/activerecord_52.gemfile
|
49
|
-
- gemfiles/activerecord_60.gemfile
|
50
|
-
- gemfiles/activerecord_61.gemfile
|
51
|
-
- gemfiles/activerecord_70.gemfile
|
52
|
-
- gemfiles/activerecord_head.gemfile
|
53
38
|
- lib/generators/online_migrations/background_migration_generator.rb
|
54
39
|
- lib/generators/online_migrations/install_generator.rb
|
55
40
|
- lib/generators/online_migrations/templates/background_migration.rb.tt
|
@@ -69,6 +54,7 @@ files:
|
|
69
54
|
- lib/online_migrations/background_migrations/migration_job_status_validator.rb
|
70
55
|
- lib/online_migrations/background_migrations/migration_runner.rb
|
71
56
|
- lib/online_migrations/background_migrations/migration_status_validator.rb
|
57
|
+
- lib/online_migrations/background_migrations/reset_counters.rb
|
72
58
|
- lib/online_migrations/background_migrations/scheduler.rb
|
73
59
|
- lib/online_migrations/batch_iterator.rb
|
74
60
|
- lib/online_migrations/change_column_type_helpers.rb
|
@@ -90,7 +76,6 @@ files:
|
|
90
76
|
- lib/online_migrations/utils.rb
|
91
77
|
- lib/online_migrations/verbose_sql_logs.rb
|
92
78
|
- lib/online_migrations/version.rb
|
93
|
-
- online_migrations.gemspec
|
94
79
|
homepage: https://github.com/fatkodima/online_migrations
|
95
80
|
licenses:
|
96
81
|
- MIT
|
data/.github/workflows/test.yml
DELETED
@@ -1,112 +0,0 @@
|
|
1
|
-
name: Test
|
2
|
-
on: [push, pull_request]
|
3
|
-
|
4
|
-
jobs:
|
5
|
-
# Run the linter first for rapid feedback if some trivial stylistic issues
|
6
|
-
# slipped through the cracks.
|
7
|
-
lint:
|
8
|
-
runs-on: ubuntu-latest
|
9
|
-
steps:
|
10
|
-
- uses: actions/checkout@v2
|
11
|
-
- uses: ruby/setup-ruby@v1
|
12
|
-
with:
|
13
|
-
ruby-version: 3.1.0
|
14
|
-
bundler-cache: true
|
15
|
-
- run: bundle exec rubocop
|
16
|
-
|
17
|
-
test:
|
18
|
-
needs: lint
|
19
|
-
runs-on: ubuntu-latest
|
20
|
-
services:
|
21
|
-
postgres:
|
22
|
-
image: postgres
|
23
|
-
env:
|
24
|
-
POSTGRES_DB: online_migrations_test
|
25
|
-
POSTGRES_PASSWORD: postgres
|
26
|
-
options: >-
|
27
|
-
--health-cmd pg_isready
|
28
|
-
--health-interval 10s
|
29
|
-
--health-timeout 5s
|
30
|
-
--health-retries 5
|
31
|
-
ports:
|
32
|
-
- 5432:5432
|
33
|
-
strategy:
|
34
|
-
matrix:
|
35
|
-
include:
|
36
|
-
- ruby-version: 2.4.10
|
37
|
-
gemfile: gemfiles/activerecord_42.gemfile
|
38
|
-
- ruby-version: 2.5.9
|
39
|
-
gemfile: gemfiles/activerecord_42.gemfile
|
40
|
-
- ruby-version: 2.6.9
|
41
|
-
gemfile: gemfiles/activerecord_42.gemfile
|
42
|
-
|
43
|
-
- ruby-version: 2.4.10
|
44
|
-
gemfile: gemfiles/activerecord_50.gemfile
|
45
|
-
- ruby-version: 2.5.9
|
46
|
-
gemfile: gemfiles/activerecord_50.gemfile
|
47
|
-
- ruby-version: 2.6.9
|
48
|
-
gemfile: gemfiles/activerecord_50.gemfile
|
49
|
-
- ruby-version: 2.7.5
|
50
|
-
gemfile: gemfiles/activerecord_50.gemfile
|
51
|
-
|
52
|
-
- ruby-version: 2.4.10
|
53
|
-
gemfile: gemfiles/activerecord_51.gemfile
|
54
|
-
- ruby-version: 2.5.9
|
55
|
-
gemfile: gemfiles/activerecord_51.gemfile
|
56
|
-
- ruby-version: 2.6.9
|
57
|
-
gemfile: gemfiles/activerecord_51.gemfile
|
58
|
-
- ruby-version: 2.7.5
|
59
|
-
gemfile: gemfiles/activerecord_51.gemfile
|
60
|
-
|
61
|
-
- ruby-version: 2.4.10
|
62
|
-
gemfile: gemfiles/activerecord_52.gemfile
|
63
|
-
- ruby-version: 2.5.9
|
64
|
-
gemfile: gemfiles/activerecord_52.gemfile
|
65
|
-
- ruby-version: 2.6.9
|
66
|
-
gemfile: gemfiles/activerecord_52.gemfile
|
67
|
-
- ruby-version: 2.7.5
|
68
|
-
gemfile: gemfiles/activerecord_52.gemfile
|
69
|
-
|
70
|
-
- ruby-version: 2.5.9
|
71
|
-
gemfile: gemfiles/activerecord_60.gemfile
|
72
|
-
- ruby-version: 2.6.9
|
73
|
-
gemfile: gemfiles/activerecord_60.gemfile
|
74
|
-
- ruby-version: 2.7.5
|
75
|
-
gemfile: gemfiles/activerecord_60.gemfile
|
76
|
-
- ruby-version: 3.0.3
|
77
|
-
gemfile: gemfiles/activerecord_60.gemfile
|
78
|
-
|
79
|
-
- ruby-version: 2.5.9
|
80
|
-
gemfile: gemfiles/activerecord_61.gemfile
|
81
|
-
- ruby-version: 2.6.9
|
82
|
-
gemfile: gemfiles/activerecord_61.gemfile
|
83
|
-
- ruby-version: 2.7.4
|
84
|
-
gemfile: gemfiles/activerecord_61.gemfile
|
85
|
-
- ruby-version: 3.0.3
|
86
|
-
gemfile: gemfiles/activerecord_61.gemfile
|
87
|
-
- ruby-version: 3.1.0
|
88
|
-
gemfile: gemfiles/activerecord_61.gemfile
|
89
|
-
|
90
|
-
- ruby-version: 2.7.4
|
91
|
-
gemfile: gemfiles/activerecord_70.gemfile
|
92
|
-
- ruby-version: 3.0.3
|
93
|
-
gemfile: gemfiles/activerecord_70.gemfile
|
94
|
-
- ruby-version: 3.1.0
|
95
|
-
gemfile: gemfiles/activerecord_70.gemfile
|
96
|
-
|
97
|
-
- ruby-version: 2.7.4
|
98
|
-
gemfile: gemfiles/activerecord_head.gemfile
|
99
|
-
- ruby-version: 3.0.3
|
100
|
-
gemfile: gemfiles/activerecord_head.gemfile
|
101
|
-
- ruby-version: 3.1.0
|
102
|
-
gemfile: gemfiles/activerecord_head.gemfile
|
103
|
-
env:
|
104
|
-
BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}
|
105
|
-
steps:
|
106
|
-
- uses: actions/checkout@v2
|
107
|
-
- uses: ruby/setup-ruby@v1
|
108
|
-
with:
|
109
|
-
ruby-version: ${{ matrix.ruby-version }}
|
110
|
-
bundler-cache: true
|
111
|
-
- name: Run the test suite
|
112
|
-
run: bundle exec rake test
|
data/.gitignore
DELETED
data/.rubocop.yml
DELETED
@@ -1,120 +0,0 @@
|
|
1
|
-
AllCops:
|
2
|
-
NewCops: enable
|
3
|
-
SuggestExtensions: false
|
4
|
-
|
5
|
-
Style/StringLiterals:
|
6
|
-
EnforcedStyle: double_quotes
|
7
|
-
|
8
|
-
Style/RescueModifier:
|
9
|
-
Exclude:
|
10
|
-
- test/**/*
|
11
|
-
|
12
|
-
Style/IfUnlessModifier:
|
13
|
-
Enabled: false
|
14
|
-
|
15
|
-
Style/SymbolArray:
|
16
|
-
EnforcedStyle: brackets
|
17
|
-
|
18
|
-
Style/WordArray:
|
19
|
-
EnforcedStyle: brackets
|
20
|
-
|
21
|
-
Style/GuardClause:
|
22
|
-
Enabled: false
|
23
|
-
|
24
|
-
Style/Documentation:
|
25
|
-
Enabled: false
|
26
|
-
|
27
|
-
Style/MutableConstant:
|
28
|
-
Enabled: false
|
29
|
-
|
30
|
-
Style/MissingRespondToMissing:
|
31
|
-
Enabled: false
|
32
|
-
|
33
|
-
Style/TrailingCommaInHashLiteral:
|
34
|
-
EnforcedStyleForMultiline: comma
|
35
|
-
|
36
|
-
Style/TrailingCommaInArrayLiteral:
|
37
|
-
EnforcedStyleForMultiline: comma
|
38
|
-
|
39
|
-
Style/NumericPredicate:
|
40
|
-
Enabled: false
|
41
|
-
|
42
|
-
Style/NegatedIf:
|
43
|
-
Enabled: false
|
44
|
-
|
45
|
-
Style/ConditionalAssignment:
|
46
|
-
Enabled: false
|
47
|
-
|
48
|
-
# only for ruby 2.3+
|
49
|
-
Style/SafeNavigation:
|
50
|
-
Enabled: false
|
51
|
-
|
52
|
-
Style/NumericLiterals:
|
53
|
-
Enabled: false
|
54
|
-
|
55
|
-
Style/Next:
|
56
|
-
Enabled: false
|
57
|
-
|
58
|
-
Style/GlobalVars:
|
59
|
-
Exclude:
|
60
|
-
- test/**/*
|
61
|
-
|
62
|
-
Style/Lambda:
|
63
|
-
EnforcedStyle: literal
|
64
|
-
|
65
|
-
Style/WhileUntilModifier:
|
66
|
-
Enabled: false
|
67
|
-
|
68
|
-
Style/HashAsLastArrayItem:
|
69
|
-
Enabled: false
|
70
|
-
|
71
|
-
# only for ruby 2.6+
|
72
|
-
Style/MapToHash:
|
73
|
-
Enabled: false
|
74
|
-
|
75
|
-
# we can not use new syntax for older rubies
|
76
|
-
Lint/ErbNewArguments:
|
77
|
-
Enabled: false
|
78
|
-
|
79
|
-
Lint/MissingSuper:
|
80
|
-
Enabled: false
|
81
|
-
|
82
|
-
Layout/EmptyLinesAroundAccessModifier:
|
83
|
-
EnforcedStyle: only_before
|
84
|
-
|
85
|
-
Layout/IndentationConsistency:
|
86
|
-
EnforcedStyle: indented_internal_methods
|
87
|
-
|
88
|
-
Layout/ArgumentAlignment:
|
89
|
-
Enabled: false
|
90
|
-
|
91
|
-
Layout/LineLength:
|
92
|
-
Enabled: false
|
93
|
-
|
94
|
-
Layout/MultilineMethodCallIndentation:
|
95
|
-
Enabled: false
|
96
|
-
|
97
|
-
Layout/HashAlignment:
|
98
|
-
Enabled: false
|
99
|
-
|
100
|
-
Naming/VariableNumber:
|
101
|
-
Exclude:
|
102
|
-
- test/**/*
|
103
|
-
|
104
|
-
Naming/MethodParameterName:
|
105
|
-
AllowedNames:
|
106
|
-
- of
|
107
|
-
- fk
|
108
|
-
|
109
|
-
Naming/AccessorMethodName:
|
110
|
-
Exclude:
|
111
|
-
- test/**/*
|
112
|
-
|
113
|
-
Gemspec/RequiredRubyVersion:
|
114
|
-
Enabled: false
|
115
|
-
|
116
|
-
Gemspec/RequireMFA:
|
117
|
-
Enabled: false
|
118
|
-
|
119
|
-
Metrics:
|
120
|
-
Enabled: false
|
data/.yardopts
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
--no-private --markup markdown lib/**/**.rb - README.md
|
data/Gemfile
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
source "https://rubygems.org"
|
4
|
-
|
5
|
-
# Specify your gem's dependencies in online_migrations.gemspec
|
6
|
-
gemspec
|
7
|
-
|
8
|
-
gem "minitest", "~> 5.0"
|
9
|
-
gem "rake", "~> 12.0"
|
10
|
-
gem "yard"
|
11
|
-
|
12
|
-
if defined?(@ar_gem_requirement)
|
13
|
-
gem "activerecord", @ar_gem_requirement
|
14
|
-
gem "railties", @ar_gem_requirement
|
15
|
-
else
|
16
|
-
gem "activerecord" # latest
|
17
|
-
gem "railties" # to test generator
|
18
|
-
|
19
|
-
# Run Rubocop only on latest rubies, because it is incompatible with older versions.
|
20
|
-
gem "rubocop", "~> 1.24"
|
21
|
-
end
|
22
|
-
|
23
|
-
if defined?(@pg_gem_requirement)
|
24
|
-
gem "pg", @pg_gem_requirement
|
25
|
-
else
|
26
|
-
gem "pg", "~> 1.2"
|
27
|
-
end
|
data/Gemfile.lock
DELETED
@@ -1,108 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
online_migrations (0.3.0)
|
5
|
-
activerecord (>= 4.2)
|
6
|
-
|
7
|
-
GEM
|
8
|
-
remote: https://rubygems.org/
|
9
|
-
specs:
|
10
|
-
actionpack (7.0.2)
|
11
|
-
actionview (= 7.0.2)
|
12
|
-
activesupport (= 7.0.2)
|
13
|
-
rack (~> 2.0, >= 2.2.0)
|
14
|
-
rack-test (>= 0.6.3)
|
15
|
-
rails-dom-testing (~> 2.0)
|
16
|
-
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
17
|
-
actionview (7.0.2)
|
18
|
-
activesupport (= 7.0.2)
|
19
|
-
builder (~> 3.1)
|
20
|
-
erubi (~> 1.4)
|
21
|
-
rails-dom-testing (~> 2.0)
|
22
|
-
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
23
|
-
activemodel (7.0.2)
|
24
|
-
activesupport (= 7.0.2)
|
25
|
-
activerecord (7.0.2)
|
26
|
-
activemodel (= 7.0.2)
|
27
|
-
activesupport (= 7.0.2)
|
28
|
-
activesupport (7.0.2)
|
29
|
-
concurrent-ruby (~> 1.0, >= 1.0.2)
|
30
|
-
i18n (>= 1.6, < 2)
|
31
|
-
minitest (>= 5.1)
|
32
|
-
tzinfo (~> 2.0)
|
33
|
-
ast (2.4.2)
|
34
|
-
builder (3.2.4)
|
35
|
-
concurrent-ruby (1.1.9)
|
36
|
-
crass (1.0.6)
|
37
|
-
erubi (1.10.0)
|
38
|
-
i18n (1.9.1)
|
39
|
-
concurrent-ruby (~> 1.0)
|
40
|
-
loofah (2.13.0)
|
41
|
-
crass (~> 1.0.2)
|
42
|
-
nokogiri (>= 1.5.9)
|
43
|
-
method_source (1.0.0)
|
44
|
-
mini_portile2 (2.7.1)
|
45
|
-
minitest (5.15.0)
|
46
|
-
nokogiri (1.13.1)
|
47
|
-
mini_portile2 (~> 2.7.0)
|
48
|
-
racc (~> 1.4)
|
49
|
-
parallel (1.21.0)
|
50
|
-
parser (3.1.0.0)
|
51
|
-
ast (~> 2.4.1)
|
52
|
-
pg (1.3.1)
|
53
|
-
racc (1.6.0)
|
54
|
-
rack (2.2.3)
|
55
|
-
rack-test (1.1.0)
|
56
|
-
rack (>= 1.0, < 3)
|
57
|
-
rails-dom-testing (2.0.3)
|
58
|
-
activesupport (>= 4.2.0)
|
59
|
-
nokogiri (>= 1.6)
|
60
|
-
rails-html-sanitizer (1.4.2)
|
61
|
-
loofah (~> 2.3)
|
62
|
-
railties (7.0.2)
|
63
|
-
actionpack (= 7.0.2)
|
64
|
-
activesupport (= 7.0.2)
|
65
|
-
method_source
|
66
|
-
rake (>= 12.2)
|
67
|
-
thor (~> 1.0)
|
68
|
-
zeitwerk (~> 2.5)
|
69
|
-
rainbow (3.1.1)
|
70
|
-
rake (12.3.3)
|
71
|
-
regexp_parser (2.2.0)
|
72
|
-
rexml (3.2.5)
|
73
|
-
rubocop (1.25.1)
|
74
|
-
parallel (~> 1.10)
|
75
|
-
parser (>= 3.1.0.0)
|
76
|
-
rainbow (>= 2.2.2, < 4.0)
|
77
|
-
regexp_parser (>= 1.8, < 3.0)
|
78
|
-
rexml
|
79
|
-
rubocop-ast (>= 1.15.1, < 2.0)
|
80
|
-
ruby-progressbar (~> 1.7)
|
81
|
-
unicode-display_width (>= 1.4.0, < 3.0)
|
82
|
-
rubocop-ast (1.15.1)
|
83
|
-
parser (>= 3.0.1.1)
|
84
|
-
ruby-progressbar (1.11.0)
|
85
|
-
thor (1.2.1)
|
86
|
-
tzinfo (2.0.4)
|
87
|
-
concurrent-ruby (~> 1.0)
|
88
|
-
unicode-display_width (2.1.0)
|
89
|
-
webrick (1.7.0)
|
90
|
-
yard (0.9.27)
|
91
|
-
webrick (~> 1.7.0)
|
92
|
-
zeitwerk (2.5.4)
|
93
|
-
|
94
|
-
PLATFORMS
|
95
|
-
ruby
|
96
|
-
|
97
|
-
DEPENDENCIES
|
98
|
-
activerecord
|
99
|
-
minitest (~> 5.0)
|
100
|
-
online_migrations!
|
101
|
-
pg (~> 1.2)
|
102
|
-
railties
|
103
|
-
rake (~> 12.0)
|
104
|
-
rubocop (~> 1.24)
|
105
|
-
yard
|
106
|
-
|
107
|
-
BUNDLED WITH
|
108
|
-
2.1.4
|
data/Rakefile
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "bundler/gem_tasks"
|
4
|
-
require "rake/testtask"
|
5
|
-
|
6
|
-
Rake::TestTask.new(:test) do |t|
|
7
|
-
t.libs << "test"
|
8
|
-
t.libs << "lib"
|
9
|
-
t.test_files = FileList["test/**/*_test.rb"]
|
10
|
-
end
|
11
|
-
|
12
|
-
require "rdoc/task"
|
13
|
-
|
14
|
-
RDoc::Task.new(:rdoc) do |rdoc|
|
15
|
-
rdoc.rdoc_dir = "rdoc"
|
16
|
-
rdoc.title = "OnlineMigrations"
|
17
|
-
rdoc.options << "--line-numbers"
|
18
|
-
rdoc.rdoc_files.include("README.md")
|
19
|
-
rdoc.rdoc_files.include("BACKGROUND_MIGRATIONS.md")
|
20
|
-
rdoc.rdoc_files.include("lib/**/*.rb")
|
21
|
-
end
|
22
|
-
|
23
|
-
task default: :test
|
data/online_migrations.gemspec
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "lib/online_migrations/version"
|
4
|
-
|
5
|
-
Gem::Specification.new do |spec|
|
6
|
-
spec.name = "online_migrations"
|
7
|
-
spec.version = OnlineMigrations::VERSION
|
8
|
-
spec.authors = ["fatkodima"]
|
9
|
-
spec.email = ["fatkodima123@gmail.com"]
|
10
|
-
|
11
|
-
spec.summary = "Catch unsafe PostgreSQL migrations in development and run them easier in production"
|
12
|
-
spec.homepage = "https://github.com/fatkodima/online_migrations"
|
13
|
-
spec.license = "MIT"
|
14
|
-
spec.required_ruby_version = Gem::Requirement.new(">= 2.1.0")
|
15
|
-
|
16
|
-
spec.metadata["homepage_uri"] = spec.homepage
|
17
|
-
spec.metadata["source_code_uri"] = spec.homepage
|
18
|
-
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/master/CHANGELOG.md"
|
19
|
-
|
20
|
-
# Specify which files should be added to the gem when it is released.
|
21
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
22
|
-
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
23
|
-
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
24
|
-
end
|
25
|
-
spec.require_paths = ["lib"]
|
26
|
-
|
27
|
-
spec.add_dependency "activerecord", ">= 4.2"
|
28
|
-
end
|