ridgepole 3.2.1 → 3.2.3

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: 44c06587d61c4599e0f7f0b10cdcc16829bd43122c9545a9e7441e22e5d1a5be
4
- data.tar.gz: 350526f0a2303c72901e214c9c01996062a3758581815605152ef702ffefcc16
3
+ metadata.gz: 1a307f88c8f4187e3ec20de74f563caa0041e66749b0c2bf0345724a2a15103b
4
+ data.tar.gz: b74fc6cae628ac13466fd7dde31679d518f3bcc963bb7dd5877c193720bebf25
5
5
  SHA512:
6
- metadata.gz: 0634c00dfc3a1c34f44e22d76cef892d17b5115a344ed600f5152ebba12e776668e8aa987650ee680cf8263a1cf779f39963a1b5cf2c7cbd20393d8a31c29b40
7
- data.tar.gz: e5303a3b7c53068957d7b27613cb2b608043d1cd6a7adf9ee278378d7e6227794909f32d01289f484e00c5b480613c6dd6ce18066b6cf634b6849d69e9a304b0
6
+ metadata.gz: 9b6d880d3b5f91ab5947dd416180c097df2ea99c566df051195357898ee457dbcc35a724467e43b23620d8fc62b1dba7a44460d71535ad7977f1f851a616f08b
7
+ data.tar.gz: 37b2b51fbbf5aadb34fd85d1526b30ddc550bbd85467e3183148f8f8376caf78c0b25f62ed7a32585c6f03caddae73a6e8b8dcbeb762058b609fc382b971bec9
data/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  ## 3.2
4
4
 
5
+ ### 3.2.3 (2026/08/01)
6
+
7
+ - Emit `validate_constraint` when DB is NOT VALID and DSL implies validated. [pull#718](https://github.com/ridgepole/ridgepole/pull/718)
8
+ - Ignore `validate: false` diff when existing constraint lacks `:validate`. [pull#716](https://github.com/ridgepole/ridgepole/pull/716)
9
+
10
+ ### 3.2.2 (2026/06/14)
11
+
12
+ - Fix constraint removal order when dropping columns. [pull#705](https://github.com/ridgepole/ridgepole/pull/705)
13
+
5
14
  ### 3.2.1 (2026/05/04)
6
15
 
7
16
  - Warn when an anonymous index ambiguously matches multiple DB indexes. [pull#692](https://github.com/ridgepole/ridgepole/pull/692)
data/README.md CHANGED
@@ -10,9 +10,10 @@ It defines DB schema using [Rails DSL](http://guides.rubyonrails.org/migrations.
10
10
  [![codecov](https://codecov.io/gh/ridgepole/ridgepole/graph/badge.svg)](https://codecov.io/gh/ridgepole/ridgepole)
11
11
 
12
12
  > [!TIP]
13
- > Currently developing a similar tool for PostgreSQL.
14
- >
15
- > see https://github.com/winebarrel/pistachio
13
+ > Also developing similar declarative schema tools — single Go binary, plain SQL as the schema definition:
14
+ >
15
+ > * [pistachio](https://github.com/winebarrel/pistachio): for PostgreSQL
16
+ > * [myschema](https://github.com/winebarrel/myschema): for MySQL
16
17
 
17
18
  > [!warning]
18
19
  > The order of columns when exporting has changed in Rails 8.1. https://github.com/rails/rails/pull/53281
@@ -31,11 +31,11 @@ module Ridgepole
31
31
 
32
32
  def script
33
33
  buf = StringIO.new
34
- pre_buf_for_fk = StringIO.new
35
- post_buf_for_fk = StringIO.new
34
+ pre_buf = StringIO.new
35
+ post_buf = StringIO.new
36
36
 
37
37
  (@delta[:add] || {}).each do |table_name, attrs|
38
- append_create_table(table_name, attrs, buf, post_buf_for_fk)
38
+ append_create_table(table_name, attrs, buf, post_buf)
39
39
  end
40
40
 
41
41
  (@delta[:rename] || {}).each do |table_name, attrs|
@@ -43,7 +43,7 @@ module Ridgepole
43
43
  end
44
44
 
45
45
  (@delta[:change] || {}).each do |table_name, attrs|
46
- append_change(table_name, attrs, buf, pre_buf_for_fk, post_buf_for_fk)
46
+ append_change(table_name, attrs, buf, pre_buf, post_buf)
47
47
  end
48
48
 
49
49
  (@delta[:delete] || {}).each do |table_name, attrs|
@@ -51,9 +51,9 @@ module Ridgepole
51
51
  end
52
52
 
53
53
  [
54
- pre_buf_for_fk,
54
+ pre_buf,
55
55
  buf,
56
- post_buf_for_fk
56
+ post_buf
57
57
  ].map { |b| b.string.strip }.join("\n\n").strip
58
58
  end
59
59
 
@@ -221,7 +221,7 @@ module Ridgepole
221
221
  end
222
222
  end
223
223
 
224
- def append_create_table(table_name, attrs, buf, post_buf_for_fk)
224
+ def append_create_table(table_name, attrs, buf, post_buf)
225
225
  options = attrs[:options] || {}
226
226
  definition = attrs[:definition] || {}
227
227
  indices = attrs[:indices] || {}
@@ -289,12 +289,12 @@ end
289
289
 
290
290
  unless (foreign_keys = attrs[:foreign_keys] || {}).empty?
291
291
  foreign_keys.each_value do |foreign_key_attrs|
292
- append_add_foreign_key(table_name, foreign_key_attrs, post_buf_for_fk, @options)
292
+ append_add_foreign_key(table_name, foreign_key_attrs, post_buf, @options)
293
293
  end
294
294
  end
295
295
 
296
296
  buf.puts
297
- post_buf_for_fk.puts
297
+ post_buf.puts
298
298
  end
299
299
 
300
300
  def partition_indices_for_create(definition, indices)
@@ -370,7 +370,7 @@ change_table_comment(#{table_name.inspect}, #{table_comment.inspect})
370
370
  end
371
371
  end
372
372
 
373
- def append_change(table_name, attrs, buf, pre_buf_for_fk, post_buf_for_fk)
373
+ def append_change(table_name, attrs, buf, pre_buf, post_buf)
374
374
  definition = attrs[:definition] || {}
375
375
  primary_key_definition = attrs[:primary_key_definition] || {}
376
376
  indices = attrs[:indices] || {}
@@ -393,10 +393,10 @@ change_table_comment(#{table_name.inspect}, #{table_comment.inspect})
393
393
  end
394
394
  end
395
395
 
396
- append_change_foreign_keys(table_name, foreign_keys, pre_buf_for_fk, post_buf_for_fk, @options) unless foreign_keys.empty?
397
- append_change_check_constraints(table_name, check_constraints, buf) unless check_constraints.empty?
398
- append_change_exclusion_constraints(table_name, exclusion_constraints, buf) unless exclusion_constraints.empty?
399
- append_change_unique_constraints(table_name, unique_constraints, buf) unless unique_constraints.empty?
396
+ append_change_foreign_keys(table_name, foreign_keys, pre_buf, post_buf, @options) unless foreign_keys.empty?
397
+ append_change_check_constraints(table_name, check_constraints, pre_buf, post_buf) unless check_constraints.empty?
398
+ append_change_exclusion_constraints(table_name, exclusion_constraints, pre_buf, post_buf) unless exclusion_constraints.empty?
399
+ append_change_unique_constraints(table_name, unique_constraints, pre_buf, post_buf) unless unique_constraints.empty?
400
400
 
401
401
  if table_options || table_charset || table_collation
402
402
  append_change_table_raw_options(table_name, table_options, table_charset, table_collation,
@@ -407,8 +407,8 @@ change_table_comment(#{table_name.inspect}, #{table_comment.inspect})
407
407
  append_change_column_comments(table_name, column_comments, buf) unless column_comments.empty?
408
408
 
409
409
  buf.puts
410
- pre_buf_for_fk.puts
411
- post_buf_for_fk.puts
410
+ pre_buf.puts
411
+ post_buf.puts
412
412
  end
413
413
 
414
414
  def append_change_column_comments(table_name, column_comments, buf)
@@ -560,13 +560,17 @@ remove_index(#{table_name.inspect}, #{target})
560
560
  end
561
561
  end
562
562
 
563
- def append_change_foreign_keys(table_name, delta, pre_buf_for_fk, post_buf_for_fk, options)
563
+ def append_change_foreign_keys(table_name, delta, pre_buf, post_buf, options)
564
564
  (delta[:delete] || {}).each_value do |attrs|
565
- append_remove_foreign_key(table_name, attrs, pre_buf_for_fk, options)
565
+ append_remove_foreign_key(table_name, attrs, pre_buf, options)
566
566
  end
567
567
 
568
568
  (delta[:add] || {}).each_value do |attrs|
569
- append_add_foreign_key(table_name, attrs, post_buf_for_fk, options)
569
+ append_add_foreign_key(table_name, attrs, post_buf, options)
570
+ end
571
+
572
+ (delta[:validate] || {}).each_value do |attrs|
573
+ append_validate_foreign_key(table_name, attrs, post_buf)
570
574
  end
571
575
  end
572
576
 
@@ -594,13 +598,32 @@ remove_foreign_key(#{table_name.inspect}, #{target})
594
598
  RUBY
595
599
  end
596
600
 
597
- def append_change_check_constraints(table_name, delta, buf)
601
+ def append_validate_foreign_key(table_name, attrs, buf)
602
+ attrs_options = attrs[:options] || {}
603
+ fk_name = attrs_options[:name]
604
+
605
+ target = if fk_name
606
+ "name: #{fk_name.inspect}"
607
+ else
608
+ attrs.fetch(:to_table).inspect
609
+ end
610
+
611
+ buf.puts(<<-RUBY)
612
+ validate_foreign_key(#{table_name.inspect}, #{target})
613
+ RUBY
614
+ end
615
+
616
+ def append_change_check_constraints(table_name, delta, pre_buf, post_buf)
598
617
  (delta[:delete] || {}).each_value do |attrs|
599
- append_remove_check_constraint(table_name, attrs, buf)
618
+ append_remove_check_constraint(table_name, attrs, pre_buf)
600
619
  end
601
620
 
602
621
  (delta[:add] || {}).each_value do |attrs|
603
- append_add_check_constraint(table_name, attrs, buf)
622
+ append_add_check_constraint(table_name, attrs, post_buf)
623
+ end
624
+
625
+ (delta[:validate] || {}).each_value do |attrs|
626
+ append_validate_check_constraint(table_name, attrs, post_buf)
604
627
  end
605
628
  end
606
629
 
@@ -628,13 +651,22 @@ remove_check_constraint(#{table_name.inspect}, #{expression.inspect}, **#{attrs_
628
651
  RUBY
629
652
  end
630
653
 
631
- def append_change_exclusion_constraints(table_name, delta, buf)
654
+ def append_validate_check_constraint(table_name, attrs, buf)
655
+ attrs_options = attrs[:options] || {}
656
+ name = attrs_options[:name]
657
+
658
+ buf.puts(<<-RUBY)
659
+ validate_check_constraint(#{table_name.inspect}, name: #{name.inspect})
660
+ RUBY
661
+ end
662
+
663
+ def append_change_exclusion_constraints(table_name, delta, pre_buf, post_buf)
632
664
  (delta[:delete] || {}).each_value do |attrs|
633
- append_remove_exclusion_constraint(table_name, attrs, buf)
665
+ append_remove_exclusion_constraint(table_name, attrs, pre_buf)
634
666
  end
635
667
 
636
668
  (delta[:add] || {}).each_value do |attrs|
637
- append_add_exclusion_constraint(table_name, attrs, buf)
669
+ append_add_exclusion_constraint(table_name, attrs, post_buf)
638
670
  end
639
671
  end
640
672
 
@@ -662,13 +694,13 @@ remove_exclusion_constraint(#{table_name.inspect}, #{expression.inspect}, **#{at
662
694
  RUBY
663
695
  end
664
696
 
665
- def append_change_unique_constraints(table_name, delta, buf)
697
+ def append_change_unique_constraints(table_name, delta, pre_buf, post_buf)
666
698
  (delta[:delete] || {}).each_value do |attrs|
667
- append_remove_unique_constraint(table_name, attrs, buf)
699
+ append_remove_unique_constraint(table_name, attrs, pre_buf)
668
700
  end
669
701
 
670
702
  (delta[:add] || {}).each_value do |attrs|
671
- append_add_unique_constraint(table_name, attrs, buf)
703
+ append_add_unique_constraint(table_name, attrs, post_buf)
672
704
  end
673
705
  end
674
706
 
@@ -524,13 +524,18 @@ module Ridgepole
524
524
  next if ignore_fk
525
525
 
526
526
  if from_attrs
527
- if from_attrs != to_attrs
528
- foreign_keys_delta[:add] ||= {}
529
- foreign_keys_delta[:add][foreign_key_name_or_tables] = to_attrs
527
+ if from_attrs != to_attrs && !ignorable_validate_diff?(from_attrs, to_attrs)
528
+ if validate_only_diff?(from_attrs, to_attrs)
529
+ foreign_keys_delta[:validate] ||= {}
530
+ foreign_keys_delta[:validate][foreign_key_name_or_tables] = to_attrs
531
+ else
532
+ foreign_keys_delta[:add] ||= {}
533
+ foreign_keys_delta[:add][foreign_key_name_or_tables] = to_attrs
530
534
 
531
- unless options[:merge]
532
- foreign_keys_delta[:delete] ||= {}
533
- foreign_keys_delta[:delete][foreign_key_name_or_tables] = from_attrs
535
+ unless options[:merge]
536
+ foreign_keys_delta[:delete] ||= {}
537
+ foreign_keys_delta[:delete][foreign_key_name_or_tables] = from_attrs
538
+ end
534
539
  end
535
540
  end
536
541
  else
@@ -558,12 +563,19 @@ module Ridgepole
558
563
  from_attrs = from.delete(name)
559
564
 
560
565
  if from_attrs
561
- if normalize_check_constraint(from_attrs) != normalize_check_constraint(to_attrs)
562
- check_constraints_delta[:add] ||= {}
563
- check_constraints_delta[:add][name] = to_attrs
566
+ from_normalized = normalize_check_constraint(from_attrs)
567
+ to_normalized = normalize_check_constraint(to_attrs)
568
+ if from_normalized != to_normalized && !ignorable_validate_diff?(from_normalized, to_normalized)
569
+ if validate_only_diff?(from_normalized, to_normalized)
570
+ check_constraints_delta[:validate] ||= {}
571
+ check_constraints_delta[:validate][name] = to_attrs
572
+ else
573
+ check_constraints_delta[:add] ||= {}
574
+ check_constraints_delta[:add][name] = to_attrs
564
575
 
565
- check_constraints_delta[:delete] ||= {}
566
- check_constraints_delta[:delete][name] = from_attrs
576
+ check_constraints_delta[:delete] ||= {}
577
+ check_constraints_delta[:delete][name] = from_attrs
578
+ end
567
579
  end
568
580
  else
569
581
  check_constraints_delta[:add] ||= {}
@@ -587,6 +599,33 @@ module Ridgepole
587
599
  attr
588
600
  end
589
601
 
602
+ # 'validate: false' (NOT VALID) is a strategy for ALTER TABLE ADD CONSTRAINT to avoid
603
+ # ACCESS EXCLUSIVE lock on large tables. NOT VALID state itself is persisted by the DB,
604
+ # but PostgreSQL ignores NOT VALID for constraints created inline within CREATE TABLE, so
605
+ # whether the resulting constraint is NOT VALID depends on how it was created. Restoring
606
+ # NOT VALID via DROP + ADD has no benefit, so ignore the diff when the DB dump lacks
607
+ # :validate and the DSL specifies validate: false.
608
+ def ignorable_validate_diff?(from_attrs, to_attrs)
609
+ from_options = from_attrs[:options] || {}
610
+ to_options = to_attrs[:options] || {}
611
+ return false unless to_options[:validate] == false && !from_options.key?(:validate)
612
+
613
+ from_attrs == to_attrs.merge(options: to_options.except(:validate))
614
+ end
615
+
616
+ # Reverse of ignorable_validate_diff?: DB has a NOT VALID constraint (validate: false)
617
+ # and the DSL implies validated (:validate missing or true). Emit VALIDATE CONSTRAINT
618
+ # instead of DROP + re-ADD so the full-table scan runs under SHARE UPDATE EXCLUSIVE
619
+ # (concurrent DML allowed) rather than ACCESS EXCLUSIVE lock.
620
+ def validate_only_diff?(from_attrs, to_attrs)
621
+ from_options = from_attrs[:options] || {}
622
+ to_options = to_attrs[:options] || {}
623
+ return false unless from_options[:validate] == false && to_options[:validate] != false
624
+
625
+ from_attrs.merge(options: from_options.except(:validate)) ==
626
+ to_attrs.merge(options: to_options.except(:validate))
627
+ end
628
+
590
629
  def scan_exclusion_constraints_change(from, to, table_delta)
591
630
  from = (from || {}).dup
592
631
  to = (to || {}).dup
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ridgepole
4
- VERSION = '3.2.1'
4
+ VERSION = '3.2.3'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ridgepole
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.1
4
+ version: 3.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Genki Sugawara
@@ -245,14 +245,14 @@ dependencies:
245
245
  requirements:
246
246
  - - '='
247
247
  - !ruby/object:Gem::Version
248
- version: 1.86.1
248
+ version: 1.88.2
249
249
  type: :development
250
250
  prerelease: false
251
251
  version_requirements: !ruby/object:Gem::Requirement
252
252
  requirements:
253
253
  - - '='
254
254
  - !ruby/object:Gem::Version
255
- version: 1.86.1
255
+ version: 1.88.2
256
256
  - !ruby/object:Gem::Dependency
257
257
  name: rubocop-rake
258
258
  requirement: !ruby/object:Gem::Requirement