pg_ha_migrations 2.1.1 → 2.2.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 +19 -0
- data/lib/pg_ha_migrations/safe_statements.rb +35 -6
- data/lib/pg_ha_migrations/unsafe_statements.rb +1 -1
- data/lib/pg_ha_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: b7db2dcf4e9c5703c49b53e785ec06df44f3dc94fa190c81fe30e2426ed588d9
|
|
4
|
+
data.tar.gz: 6b763dbe8f8126bf26c2682ab69e50ea54df505ba099bf0f981e11a5831dee11
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a92e9ba724c5d8faf39b0314430ea4848e2081926c1f02902e4bffb947cde09ccc2de1c4db74b5cb54ee3fb6b8db3072b88dab2ed10432965fbd612ec3d0c0c2
|
|
7
|
+
data.tar.gz: 5bf9858b2f1dc84936dae0128f4ddc43f7ae4961ae2564c5b23afdb9f9f8dbad1b9abe527aabe48bd0afca74e6f8d3a457a412f89ab54ef210dcceeaf5ba60ef
|
data/README.md
CHANGED
|
@@ -304,6 +304,25 @@ Safely validate (without acquiring an exclusive lock) existing rows for a newly
|
|
|
304
304
|
safe_validate_check_constraint :table, name: :constraint_table_on_column_like_example
|
|
305
305
|
```
|
|
306
306
|
|
|
307
|
+
#### safe\_add\_foreign\_key
|
|
308
|
+
|
|
309
|
+
Safely add a foreign key constraint. The constraint is first added as `NOT VALID` (to avoid blocking writes while the table is scanned) and then validated in a separate step that allows concurrent reads and writes.
|
|
310
|
+
|
|
311
|
+
```ruby
|
|
312
|
+
safe_add_foreign_key :orders, :customers
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
With custom options:
|
|
316
|
+
|
|
317
|
+
```ruby
|
|
318
|
+
safe_add_foreign_key :orders, :customers,
|
|
319
|
+
column: :buyer_id,
|
|
320
|
+
name: :fk_orders_buyer,
|
|
321
|
+
on_delete: :cascade
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
> **Note:** This method runs multiple DDL statements non-transactionally. The validation step performs a full table scan to verify existing rows satisfy the constraint. While no exclusive lock is held during validation, the scan may take a long time on large tables.
|
|
325
|
+
|
|
307
326
|
#### safe\_rename\_constraint
|
|
308
327
|
|
|
309
328
|
Safely rename any (not just `CHECK`) constraint.
|
|
@@ -270,7 +270,21 @@ module PgHaMigrations::SafeStatements
|
|
|
270
270
|
unless ActiveRecord::Base.connection.postgresql_version >= 9_06_00
|
|
271
271
|
raise PgHaMigrations::InvalidMigrationError, "Removing an index concurrently is not supported on Postgres 9.1 databases"
|
|
272
272
|
end
|
|
273
|
-
|
|
273
|
+
|
|
274
|
+
index_exists = select_value(
|
|
275
|
+
"SELECT EXISTS(SELECT 1 FROM pg_class WHERE relname = #{connection.quote(options[:name].to_s)} AND relkind = 'i')"
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
unless index_exists
|
|
279
|
+
if options[:if_exists]
|
|
280
|
+
say "Index #{options[:name]} does not exist, skipping removal"
|
|
281
|
+
return
|
|
282
|
+
else
|
|
283
|
+
raise ArgumentError, "Index #{options[:name]} does not exist"
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
index_size = select_value("SELECT pg_size_pretty(pg_relation_size(#{connection.quote(options[:name].to_s)}))")
|
|
274
288
|
say "Preparing to drop index #{options[:name]} which is #{index_size} on disk..."
|
|
275
289
|
unsafe_remove_index(table, **options.merge(:algorithm => :concurrently))
|
|
276
290
|
end
|
|
@@ -383,6 +397,17 @@ module PgHaMigrations::SafeStatements
|
|
|
383
397
|
end
|
|
384
398
|
end
|
|
385
399
|
|
|
400
|
+
def safe_add_foreign_key(from_table, to_table, **options)
|
|
401
|
+
# Rails convention for determining the foreign key column
|
|
402
|
+
column = options.fetch(:column) { "#{to_table.to_s.singularize}_id" }
|
|
403
|
+
|
|
404
|
+
# Rails convention for generating FK name: fk_rails_<10 char hash of column>
|
|
405
|
+
fk_name = options.fetch(:name) { "fk_rails_#{OpenSSL::Digest::SHA256.hexdigest(column.to_s).first(10)}" }
|
|
406
|
+
|
|
407
|
+
unsafe_add_foreign_key(from_table, to_table, validate: false, **options.merge(name: fk_name))
|
|
408
|
+
safe_validate_check_constraint(from_table, name: fk_name)
|
|
409
|
+
end
|
|
410
|
+
|
|
386
411
|
def safe_rename_constraint(table, from:, to:)
|
|
387
412
|
raise ArgumentError, "Expected <from> to be present" unless from.present?
|
|
388
413
|
raise ArgumentError, "Expected <to> to be present" unless to.present?
|
|
@@ -683,13 +708,15 @@ module PgHaMigrations::SafeStatements
|
|
|
683
708
|
|
|
684
709
|
def adjust_lock_timeout(timeout_seconds = PgHaMigrations::LOCK_TIMEOUT_SECONDS, &block)
|
|
685
710
|
_check_postgres_adapter!
|
|
686
|
-
|
|
711
|
+
original_timeout_ms = ActiveRecord::Base.value_from_sql(
|
|
712
|
+
"SELECT setting::integer FROM pg_settings WHERE name = 'lock_timeout'"
|
|
713
|
+
)
|
|
687
714
|
begin
|
|
688
715
|
connection.execute("SET lock_timeout = #{PG::Connection.escape_string((timeout_seconds * 1000).to_s)};")
|
|
689
716
|
block.call
|
|
690
717
|
ensure
|
|
691
718
|
begin
|
|
692
|
-
connection.execute("SET lock_timeout = #{
|
|
719
|
+
connection.execute("SET lock_timeout = #{original_timeout_ms};")
|
|
693
720
|
rescue ActiveRecord::StatementInvalid => e
|
|
694
721
|
if e.message =~ /PG::InFailedSqlTransaction/
|
|
695
722
|
# If we're in a failed transaction the `SET lock_timeout` will be rolled back,
|
|
@@ -703,16 +730,18 @@ module PgHaMigrations::SafeStatements
|
|
|
703
730
|
|
|
704
731
|
def adjust_statement_timeout(timeout_seconds, &block)
|
|
705
732
|
_check_postgres_adapter!
|
|
706
|
-
|
|
733
|
+
original_timeout_ms = ActiveRecord::Base.value_from_sql(
|
|
734
|
+
"SELECT setting::integer FROM pg_settings WHERE name = 'statement_timeout'"
|
|
735
|
+
)
|
|
707
736
|
begin
|
|
708
737
|
connection.execute("SET statement_timeout = #{PG::Connection.escape_string((timeout_seconds * 1000).to_s)};")
|
|
709
738
|
block.call
|
|
710
739
|
ensure
|
|
711
740
|
begin
|
|
712
|
-
connection.execute("SET statement_timeout = #{
|
|
741
|
+
connection.execute("SET statement_timeout = #{original_timeout_ms};")
|
|
713
742
|
rescue ActiveRecord::StatementInvalid => e
|
|
714
743
|
if e.message =~ /PG::InFailedSqlTransaction/
|
|
715
|
-
# If we're in a failed transaction the `SET
|
|
744
|
+
# If we're in a failed transaction the `SET statement_timeout` will be rolled back,
|
|
716
745
|
# so we don't need to worry about cleaning up, and we can't execute SQL anyway.
|
|
717
746
|
else
|
|
718
747
|
raise e
|
|
@@ -77,7 +77,7 @@ module PgHaMigrations::UnsafeStatements
|
|
|
77
77
|
# Otherwise, direct dispatch to underlying Rails method without dependent object check / locking
|
|
78
78
|
disable_or_delegate_default_method :add_check_constraint, ":add_check_constraint is NOT SAFE! Use :safe_add_unvalidated_check_constraint and then :safe_validate_check_constraint instead"
|
|
79
79
|
disable_or_delegate_default_method :add_column, ":add_column is NOT SAFE! Use safe_add_column instead"
|
|
80
|
-
disable_or_delegate_default_method :add_foreign_key, ":add_foreign_key is NOT SAFE!
|
|
80
|
+
disable_or_delegate_default_method :add_foreign_key, ":add_foreign_key is NOT SAFE! Use safe_add_foreign_key instead"
|
|
81
81
|
disable_or_delegate_default_method :add_index, ":add_index is NOT SAFE! Use safe_add_concurrent_index instead"
|
|
82
82
|
disable_or_delegate_default_method :change_column, ":change_column is NOT SAFE! Use a combination of safe and explicit unsafe migration methods instead"
|
|
83
83
|
disable_or_delegate_default_method :change_column_default, ":change_column_default is NOT SAFE! Use safe_change_column_default instead"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pg_ha_migrations
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- celeen
|
|
@@ -13,7 +13,7 @@ authors:
|
|
|
13
13
|
- redneckbeard
|
|
14
14
|
bindir: exe
|
|
15
15
|
cert_chain: []
|
|
16
|
-
date:
|
|
16
|
+
date: 2026-01-08 00:00:00.000000000 Z
|
|
17
17
|
dependencies:
|
|
18
18
|
- !ruby/object:Gem::Dependency
|
|
19
19
|
name: rake
|