nandi 3.1.0 → 3.3.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/README.md +21 -0
- data/lib/nandi/config.rb +2 -2
- data/lib/nandi/instructions/extend_varchar_column_limit.rb +36 -0
- data/lib/nandi/instructions.rb +1 -0
- data/lib/nandi/migration.rb +30 -5
- data/lib/nandi/multi_database.rb +23 -2
- data/lib/nandi/renderers/active_record/instructions.rb +10 -0
- data/lib/nandi/version.rb +1 -1
- data/lib/templates/nandi/renderers/active_record/instructions/extend_varchar_column_limit/show.rb.erb +16 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ab45002075955602da710e92a39cc3b9fe8bdc9371a33e97319e9e24d68a3385
|
|
4
|
+
data.tar.gz: 25f3e29b999a37eeb5d46ea272b12e313ae11de5c272dd81120dde4d2dd6ecfe
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a916d9acc8c50060a472e2c6630529ac6b6832380d2198f1d8e19785ce7da5ff211e24e44b33ce51d6f305fab24e98ba7297cba55904f89d394cc939cb202883
|
|
7
|
+
data.tar.gz: 6b1674699aa6e907474c00d31c6a337ae4af7d9e29d1528811e8dcc19e2235a3340fd59ae1f4f1b4176f21c882508e6d20ec992a77b292d7c31a78c05f671315
|
data/README.md
CHANGED
|
@@ -315,6 +315,10 @@ Drops an existing NOT NULL constraint. Please not that this migration is not saf
|
|
|
315
315
|
|
|
316
316
|
Changes the default value for this column when new rows are inserted into the table.
|
|
317
317
|
|
|
318
|
+
### `#extend_varchar_column_limit(table, column, from:, to:)`
|
|
319
|
+
|
|
320
|
+
Widens (or removes, with `to: nil`) the character limit on an existing varchar column. Since Postgres 9.2 this is metadata-only; _narrowing_ a limit requires a full-table scan and isn't supported. The generated migration re-checks the column's actual type and limit in the database against `from:` before changing it, so this is safe to recompile even after it has already run. Not safely reversible: `down` should be `irreversible_migration`.
|
|
321
|
+
|
|
318
322
|
### `#remove_index(table, target)`
|
|
319
323
|
|
|
320
324
|
Drop an index from the database.
|
|
@@ -566,6 +570,9 @@ These options can be set individually for each database. **All are optional** -
|
|
|
566
570
|
- `access_exclusive_statement_timeout_limit`: Maximum allowed statement timeout (default: 1,500ms)
|
|
567
571
|
- `concurrent_lock_timeout_limit`: Minimum timeout for concurrent operations (default: 3,600,000ms / 1 hour)
|
|
568
572
|
- `concurrent_statement_timeout_limit`: Minimum statement timeout for concurrent operations (default: 3,600,000ms / 1 hour)
|
|
573
|
+
- `concurrent_lock_timeout`: Lock timeout for concurrent operations (`add_index`/`remove_index`). When set, concurrent migrations use `set_lock_timeout` instead of `disable_lock_timeout!`. Default: `nil` (timeout disabled)
|
|
574
|
+
- `concurrent_statement_timeout`: Statement timeout for concurrent operations. When set, concurrent migrations use `set_statement_timeout` instead of `disable_statement_timeout!`. Default: `nil` (timeout disabled)
|
|
575
|
+
- `table_overrides`: Per-table overrides of `concurrent_lock_timeout`/`concurrent_statement_timeout`, keyed by table name. Falls back to the database-level value for any table (or key) not listed
|
|
569
576
|
|
|
570
577
|
**Global options** (set via config accessors):
|
|
571
578
|
|
|
@@ -621,6 +628,20 @@ Nandi.configure do |config|
|
|
|
621
628
|
migration
|
|
622
629
|
end
|
|
623
630
|
end
|
|
631
|
+
|
|
632
|
+
# Give a specific, huge table a longer concurrent statement/lock timeout than
|
|
633
|
+
# the database-wide default, without loosening it for every other table.
|
|
634
|
+
Nandi.configure do |config|
|
|
635
|
+
config.register_database(:primary,
|
|
636
|
+
concurrent_lock_timeout: 120_000, # 2 minutes, database-wide default
|
|
637
|
+
concurrent_statement_timeout: 600_000, # 10 minutes, database-wide default
|
|
638
|
+
table_overrides: {
|
|
639
|
+
payments: {
|
|
640
|
+
concurrent_lock_timeout: 300_000, # 5 minutes
|
|
641
|
+
concurrent_statement_timeout: 1_800_000, # 30 minutes
|
|
642
|
+
},
|
|
643
|
+
})
|
|
644
|
+
end
|
|
624
645
|
```
|
|
625
646
|
|
|
626
647
|
### Directory Structure
|
data/lib/nandi/config.rb
CHANGED
|
@@ -84,8 +84,8 @@ module Nandi
|
|
|
84
84
|
def access_exclusive_statement_timeout_max(database_name = nil) = config(database_name).access_exclusive_statement_timeout_max
|
|
85
85
|
def concurrent_lock_timeout_min(database_name = nil) = config(database_name).concurrent_lock_timeout_min
|
|
86
86
|
def concurrent_statement_timeout_min(database_name = nil) = config(database_name).concurrent_statement_timeout_min
|
|
87
|
-
def concurrent_lock_timeout(database_name = nil) = config(database_name).concurrent_lock_timeout
|
|
88
|
-
def concurrent_statement_timeout(database_name = nil) = config(database_name).concurrent_statement_timeout
|
|
87
|
+
def concurrent_lock_timeout(database_name = nil, table_name = nil) = config(database_name).concurrent_lock_timeout(table_name)
|
|
88
|
+
def concurrent_statement_timeout(database_name = nil, table_name = nil) = config(database_name).concurrent_statement_timeout(table_name)
|
|
89
89
|
# rubocop:enable Layout/LineLength
|
|
90
90
|
|
|
91
91
|
# Delegate setter methods to the default database for backwards compatibility
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Nandi
|
|
4
|
+
module Instructions
|
|
5
|
+
class ExtendVarcharColumnLimit
|
|
6
|
+
attr_reader :table, :column, :from, :to
|
|
7
|
+
|
|
8
|
+
def initialize(table:, column:, from:, to:)
|
|
9
|
+
unless from.is_a?(Integer)
|
|
10
|
+
raise ArgumentError, "extend_varchar_column_limit requires an integer `from:` limit"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
unless to.nil? || (to.is_a?(Integer) && to > from)
|
|
14
|
+
raise ArgumentError,
|
|
15
|
+
"extend_varchar_column_limit only supports widening a character limit, or " \
|
|
16
|
+
"removing it entirely (to: nil), but #{table}.#{column} would go from #{from} " \
|
|
17
|
+
"to #{to.inspect}. Narrowing a limit requires a full-table scan under an " \
|
|
18
|
+
"ACCESS EXCLUSIVE lock and isn't supported here."
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
@table = table
|
|
22
|
+
@column = column
|
|
23
|
+
@from = from
|
|
24
|
+
@to = to
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def procedure
|
|
28
|
+
:extend_varchar_column_limit
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def lock
|
|
32
|
+
Nandi::Migration::LockWeights::ACCESS_EXCLUSIVE
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
data/lib/nandi/instructions.rb
CHANGED
|
@@ -12,6 +12,7 @@ require "nandi/instructions/add_foreign_key"
|
|
|
12
12
|
require "nandi/instructions/drop_constraint"
|
|
13
13
|
require "nandi/instructions/remove_not_null_constraint"
|
|
14
14
|
require "nandi/instructions/change_column_default"
|
|
15
|
+
require "nandi/instructions/extend_varchar_column_limit"
|
|
15
16
|
require "nandi/instructions/validate_constraint"
|
|
16
17
|
require "nandi/instructions/add_check_constraint"
|
|
17
18
|
require "nandi/instructions/irreversible_migration"
|
data/lib/nandi/migration.rb
CHANGED
|
@@ -311,6 +311,19 @@ module Nandi
|
|
|
311
311
|
)
|
|
312
312
|
end
|
|
313
313
|
|
|
314
|
+
# Widens (or removes) the character limit on an existing varchar column.
|
|
315
|
+
# Since Postgres 9.2 this is metadata-only (a brief ACCESS EXCLUSIVE lock).
|
|
316
|
+
# Narrowing remains a different, unsafe operation and is not supported.
|
|
317
|
+
#
|
|
318
|
+
# Not safely reversible: `down` should be `irreversible_migration`.
|
|
319
|
+
# @param table [Symbol, String] The name of the table with the column
|
|
320
|
+
# @param column [Symbol, String] The name of the column to change
|
|
321
|
+
# @param from [Integer] The column's current character limit
|
|
322
|
+
# @param to [Integer, nil] The new character limit, or nil to remove it entirely
|
|
323
|
+
def extend_varchar_column_limit(table, column, from:, to:)
|
|
324
|
+
current_instructions << Instructions::ExtendVarcharColumnLimit.new(table:, column:, from:, to:)
|
|
325
|
+
end
|
|
326
|
+
|
|
314
327
|
# Raises an `ActiveRecord::IrreversibleMigration` error for use in
|
|
315
328
|
# irreversible migrations
|
|
316
329
|
def irreversible_migration
|
|
@@ -337,7 +350,7 @@ module Nandi
|
|
|
337
350
|
|
|
338
351
|
def disable_lock_timeout?
|
|
339
352
|
if self.class.lock_timeout.nil?
|
|
340
|
-
strictest_lock == LockWeights::SHARE && Nandi.config.concurrent_lock_timeout(database_name).nil?
|
|
353
|
+
strictest_lock == LockWeights::SHARE && Nandi.config.concurrent_lock_timeout(database_name, table).nil?
|
|
341
354
|
else
|
|
342
355
|
false
|
|
343
356
|
end
|
|
@@ -345,7 +358,7 @@ module Nandi
|
|
|
345
358
|
|
|
346
359
|
def disable_statement_timeout?
|
|
347
360
|
if self.class.statement_timeout.nil?
|
|
348
|
-
strictest_lock == LockWeights::SHARE && Nandi.config.concurrent_statement_timeout(database_name).nil?
|
|
361
|
+
strictest_lock == LockWeights::SHARE && Nandi.config.concurrent_statement_timeout(database_name, table).nil?
|
|
349
362
|
else
|
|
350
363
|
false
|
|
351
364
|
end
|
|
@@ -360,7 +373,7 @@ module Nandi
|
|
|
360
373
|
end
|
|
361
374
|
|
|
362
375
|
def mixins
|
|
363
|
-
|
|
376
|
+
all_instructions.inject([]) do |mixins, i|
|
|
364
377
|
i.respond_to?(:mixins) ? [*mixins, *i.mixins] : mixins
|
|
365
378
|
end.uniq
|
|
366
379
|
end
|
|
@@ -383,7 +396,7 @@ module Nandi
|
|
|
383
396
|
|
|
384
397
|
def default_statement_timeout
|
|
385
398
|
if strictest_lock == LockWeights::SHARE
|
|
386
|
-
Nandi.config.concurrent_statement_timeout(database_name) ||
|
|
399
|
+
Nandi.config.concurrent_statement_timeout(database_name, table) ||
|
|
387
400
|
Nandi.config.access_exclusive_statement_timeout(database_name)
|
|
388
401
|
else
|
|
389
402
|
Nandi.config.access_exclusive_statement_timeout(database_name)
|
|
@@ -392,12 +405,24 @@ module Nandi
|
|
|
392
405
|
|
|
393
406
|
def default_lock_timeout
|
|
394
407
|
if strictest_lock == LockWeights::SHARE
|
|
395
|
-
Nandi.config.concurrent_lock_timeout(database_name) ||
|
|
408
|
+
Nandi.config.concurrent_lock_timeout(database_name, table) ||
|
|
409
|
+
Nandi.config.access_exclusive_lock_timeout(database_name)
|
|
396
410
|
else
|
|
397
411
|
Nandi.config.access_exclusive_lock_timeout(database_name)
|
|
398
412
|
end
|
|
399
413
|
end
|
|
400
414
|
|
|
415
|
+
# The table this migration modifies, if any. Validator guarantees a migration
|
|
416
|
+
# modifies at most one table, so this is unambiguous.
|
|
417
|
+
def table
|
|
418
|
+
instruction_with_table = all_instructions.find { |i| i.respond_to?(:table) }
|
|
419
|
+
instruction_with_table&.table&.to_sym
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
def all_instructions
|
|
423
|
+
up_instructions + down_instructions
|
|
424
|
+
end
|
|
425
|
+
|
|
401
426
|
def invoke_custom_method(name, ...)
|
|
402
427
|
klass = Nandi.config.custom_methods[name]
|
|
403
428
|
current_instructions << klass.new(...)
|
data/lib/nandi/multi_database.rb
CHANGED
|
@@ -54,14 +54,24 @@ module Nandi
|
|
|
54
54
|
# The default lock timeout for migrations that take place concurrently
|
|
55
55
|
# (eg. add_index, remove_index). When set, concurrent migrations will use
|
|
56
56
|
# set_lock_timeout instead of disable_lock_timeout!. Default: nil (disabled).
|
|
57
|
+
# Can be overridden for a specific table via `table_overrides`.
|
|
58
|
+
# @param table_name [Symbol, String, nil]
|
|
57
59
|
# @return [Integer, nil]
|
|
58
|
-
|
|
60
|
+
def concurrent_lock_timeout(table_name = nil)
|
|
61
|
+
table_override(table_name, :concurrent_lock_timeout) || @concurrent_lock_timeout
|
|
62
|
+
end
|
|
63
|
+
attr_writer :concurrent_lock_timeout
|
|
59
64
|
|
|
60
65
|
# The default statement timeout for migrations that take place concurrently
|
|
61
66
|
# (eg. add_index, remove_index). When set, concurrent migrations will use
|
|
62
67
|
# set_statement_timeout instead of disable_statement_timeout!. Default: nil (disabled).
|
|
68
|
+
# Can be overridden for a specific table via `table_overrides`.
|
|
69
|
+
# @param table_name [Symbol, String, nil]
|
|
63
70
|
# @return [Integer, nil]
|
|
64
|
-
|
|
71
|
+
def concurrent_statement_timeout(table_name = nil)
|
|
72
|
+
table_override(table_name, :concurrent_statement_timeout) || @concurrent_statement_timeout
|
|
73
|
+
end
|
|
74
|
+
attr_writer :concurrent_statement_timeout
|
|
65
75
|
|
|
66
76
|
# The directory for output files. Default: `db/migrate`
|
|
67
77
|
# @return [String]
|
|
@@ -121,6 +131,17 @@ module Nandi
|
|
|
121
131
|
config[:concurrent_statement_timeout_min] || DEFAULT_CONCURRENT_STATEMENT_TIMEOUT_MIN
|
|
122
132
|
@concurrent_lock_timeout = config[:concurrent_lock_timeout]
|
|
123
133
|
@concurrent_statement_timeout = config[:concurrent_statement_timeout]
|
|
134
|
+
@table_overrides = normalize_table_overrides(config[:table_overrides])
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def normalize_table_overrides(table_overrides)
|
|
138
|
+
(table_overrides || {}).transform_keys(&:to_sym)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def table_override(table_name, key)
|
|
142
|
+
return nil if table_name.nil?
|
|
143
|
+
|
|
144
|
+
@table_overrides.dig(table_name.to_sym, key)
|
|
124
145
|
end
|
|
125
146
|
|
|
126
147
|
def path_prefix(name, default)
|
|
@@ -105,6 +105,16 @@ module Nandi
|
|
|
105
105
|
formatted_property :value
|
|
106
106
|
end
|
|
107
107
|
|
|
108
|
+
class ExtendVarcharColumnLimitCell < Base
|
|
109
|
+
property :table
|
|
110
|
+
property :column
|
|
111
|
+
property :from
|
|
112
|
+
|
|
113
|
+
def to_type_sql
|
|
114
|
+
model.to.nil? ? "character varying" : "character varying(#{model.to})"
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
108
118
|
class RemoveNotNullConstraintCell < Base
|
|
109
119
|
formatted_property :table
|
|
110
120
|
formatted_property :column
|
data/lib/nandi/version.rb
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
execute <<-SQL
|
|
2
|
+
DO $$
|
|
3
|
+
DECLARE
|
|
4
|
+
actual_type text;
|
|
5
|
+
BEGIN
|
|
6
|
+
SELECT format_type(atttypid, atttypmod) INTO actual_type
|
|
7
|
+
FROM pg_attribute
|
|
8
|
+
WHERE attrelid = '<%= table %>'::regclass AND attname = '<%= column %>' AND NOT attisdropped;
|
|
9
|
+
|
|
10
|
+
IF actual_type IS DISTINCT FROM 'character varying(<%= from %>)' THEN
|
|
11
|
+
RAISE EXCEPTION 'expected <%= table %>.<%= column %> to be character varying(<%= from %>), found %', actual_type;
|
|
12
|
+
END IF;
|
|
13
|
+
END $$;
|
|
14
|
+
|
|
15
|
+
ALTER TABLE <%= table %> ALTER COLUMN "<%= column %>" TYPE <%= to_type_sql %>;
|
|
16
|
+
SQL
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: nandi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- GoCardless Engineering
|
|
@@ -112,6 +112,7 @@ files:
|
|
|
112
112
|
- lib/nandi/instructions/create_table.rb
|
|
113
113
|
- lib/nandi/instructions/drop_constraint.rb
|
|
114
114
|
- lib/nandi/instructions/drop_table.rb
|
|
115
|
+
- lib/nandi/instructions/extend_varchar_column_limit.rb
|
|
115
116
|
- lib/nandi/instructions/irreversible_migration.rb
|
|
116
117
|
- lib/nandi/instructions/remove_column.rb
|
|
117
118
|
- lib/nandi/instructions/remove_index.rb
|
|
@@ -155,6 +156,7 @@ files:
|
|
|
155
156
|
- lib/templates/nandi/renderers/active_record/instructions/create_table/show.rb.erb
|
|
156
157
|
- lib/templates/nandi/renderers/active_record/instructions/drop_constraint/show.rb.erb
|
|
157
158
|
- lib/templates/nandi/renderers/active_record/instructions/drop_table/show.rb.erb
|
|
159
|
+
- lib/templates/nandi/renderers/active_record/instructions/extend_varchar_column_limit/show.rb.erb
|
|
158
160
|
- lib/templates/nandi/renderers/active_record/instructions/irreversible_migration/show.rb.erb
|
|
159
161
|
- lib/templates/nandi/renderers/active_record/instructions/remove_column/show.rb.erb
|
|
160
162
|
- lib/templates/nandi/renderers/active_record/instructions/remove_index/show.rb.erb
|
|
@@ -180,7 +182,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
180
182
|
- !ruby/object:Gem::Version
|
|
181
183
|
version: '0'
|
|
182
184
|
requirements: []
|
|
183
|
-
rubygems_version: 4.0.
|
|
185
|
+
rubygems_version: 4.0.20
|
|
184
186
|
specification_version: 4
|
|
185
187
|
summary: Fear-free migrations for PostgreSQL
|
|
186
188
|
test_files: []
|