ridgepole 3.2.2 → 3.2.4

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: aa8b60187f54d3f6f510aae6326606c4d0885c3867cc48c6b51d27fd5ac1c5e1
4
- data.tar.gz: 5c52635dff367490eeb5ebda435ea26e4c90f784ef4036c21fb6d58865b93674
3
+ metadata.gz: 30883571738ca7fb36863b862ea2c3c447dd2b557065c60c1c1a21a66beddb42
4
+ data.tar.gz: 425b7bc6cc0f9efffa9ea3b02fe681db1a242d1d2b41fcceee2d463082a7c866
5
5
  SHA512:
6
- metadata.gz: 54c4b813601a1bc68891456a7d95ab41641dc63270ca22b0652f06d6757d49b6740a6c0738cc3f5f28c69c5d80c22bd2ecb0b7e7d7de12e6c321f591b95db6cd
7
- data.tar.gz: df2418e5c2bb8ebfe5759c04d3656e524f3b94e995ba24c67f6e74629bdce00667c324d0265433a7333b335ab9245b133c5212a5f0dc282a57692bb6c83b41cf
6
+ metadata.gz: 6e716a0ade803f5ae29aec8f861f06009aaec35274fcc4fbb28984525d956ca7c2a4681700938ce04906f1e192e6acf4838cbf37c50739b15d75c88743d7b4ba
7
+ data.tar.gz: 1a208c2dadeda2e1ccc0c250223f51351688b35b8046c5819998fe4249fb507e973b30310648fc12f3b7b84a4fb584771622fa91f2828eff57919b2a79f5ab70
data/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  ## 3.2
4
4
 
5
+ ### 3.2.4 (2026/08/08)
6
+
7
+ - Fix inverted "No change" log in `--diff --with-apply`. [pull#730](https://github.com/ridgepole/ridgepole/pull/730)
8
+
9
+ ### 3.2.3 (2026/08/01)
10
+
11
+ - Emit `validate_constraint` when DB is NOT VALID and DSL implies validated. [pull#718](https://github.com/ridgepole/ridgepole/pull/718)
12
+ - Ignore `validate: false` diff when existing constraint lacks `:validate`. [pull#716](https://github.com/ridgepole/ridgepole/pull/716)
13
+
5
14
  ### 3.2.2 (2026/06/14)
6
15
 
7
16
  - Fix constraint removal order when dropping columns. [pull#705](https://github.com/ridgepole/ridgepole/pull/705)
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Ridgepole
1
+ ![](https://github.com/user-attachments/assets/e446e211-054a-43d8-978b-c52ae6f1ea93)
2
2
 
3
3
  Ridgepole is a tool to manage DB schema.
4
4
 
@@ -10,17 +10,18 @@ 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
- > Also developing similar declarative schema tools — single Go binary, plain SQL as the schema definition:
13
+ > Also developing similar declarative schema tools — single Go binary, plain SQL as the schema definition. Give it a try.
14
14
  >
15
15
  > * [pistachio](https://github.com/winebarrel/pistachio): for PostgreSQL
16
- > * [myschema](https://github.com/winebarrel/myschema): for MySQL
17
16
 
18
17
  > [!warning]
19
18
  > The order of columns when exporting has changed in Rails 8.1. https://github.com/rails/rails/pull/53281
20
19
  >
21
20
  > If you do not want to sort the columns, use `--disable-sort-columns` option.
22
21
 
23
- > [!note]
22
+ <details>
23
+ <summary>Note</summary>
24
+
24
25
  > * ridgepole v3.2.0
25
26
  > * Use `udiff` instead of `diffy` (cf. https://github.com/ridgepole/ridgepole/pull/657)
26
27
  > * ridgepole v3.1.0
@@ -49,6 +50,8 @@ It defines DB schema using [Rails DSL](http://guides.rubyonrails.org/migrations.
49
50
  > * cf. https://github.com/rails/rails/commit/c2a6f618d22cca4d9b7be7fa7652e7aac509350c#diff-55f41513f027a3d219629f475f03c2d1105ca55c5093d691e1b3dc4710c6cc0b
50
51
  > * SQLite does not support.
51
52
 
53
+ </details>
54
+
52
55
  ## Installation
53
56
 
54
57
  Add this line to your application's Gemfile:
@@ -102,6 +105,7 @@ Usage: ridgepole [options]
102
105
  --drop-table-only
103
106
  --mysql-change-table-options
104
107
  --mysql-change-table-comment
108
+ --pg-change-table-comment
105
109
  --check-relation-type DEF_PK
106
110
  --ignore-table-comment
107
111
  --skip-column-comment-change
@@ -286,6 +290,26 @@ create_table "users", force: :cascade do |t|
286
290
  end
287
291
  ```
288
292
 
293
+ ## Functional Index (MySQL)
294
+
295
+ MySQL 8.0.13 or later supports functional key parts, also known as expression
296
+ indexes. MySQL normalizes these expressions when storing them, and Ridgepole
297
+ compares the normalized expression with the one in your Schemafile. To avoid
298
+ repeated diffs, use the expression produced by `ridgepole --export` in your
299
+ Schemafile.
300
+
301
+ ```ruby
302
+ create_table "users", force: :cascade do |t|
303
+ t.datetime "deleted_at"
304
+ t.string "email", null: false
305
+ t.string "name", null: false
306
+
307
+ t.index "(lower(`name`))", name: "index_users_on_lower_name"
308
+ t.index "(case when (`deleted_at` is null) then `email` else NULL end)",
309
+ name: "index_users_on_active_email", unique: true
310
+ end
311
+ ```
312
+
289
313
  ## Execute
290
314
  ```ruby
291
315
  create_table "authors", force: :cascade do |t|
data/bin/ridgepole CHANGED
@@ -290,7 +290,7 @@ begin
290
290
 
291
291
  differ, _out = delta.migrate if differ
292
292
 
293
- logger.info('No change') if differ
293
+ logger.info('No change') unless differ
294
294
  elsif delta.differ?
295
295
  differ = noop_migrate(delta, options)
296
296
  exit_code = 1 if differ
@@ -568,6 +568,10 @@ remove_index(#{table_name.inspect}, #{target})
568
568
  (delta[:add] || {}).each_value do |attrs|
569
569
  append_add_foreign_key(table_name, attrs, post_buf, options)
570
570
  end
571
+
572
+ (delta[:validate] || {}).each_value do |attrs|
573
+ append_validate_foreign_key(table_name, attrs, post_buf)
574
+ end
571
575
  end
572
576
 
573
577
  def append_add_foreign_key(table_name, attrs, buf, _options)
@@ -594,6 +598,21 @@ remove_foreign_key(#{table_name.inspect}, #{target})
594
598
  RUBY
595
599
  end
596
600
 
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
+
597
616
  def append_change_check_constraints(table_name, delta, pre_buf, post_buf)
598
617
  (delta[:delete] || {}).each_value do |attrs|
599
618
  append_remove_check_constraint(table_name, attrs, pre_buf)
@@ -602,6 +621,10 @@ remove_foreign_key(#{table_name.inspect}, #{target})
602
621
  (delta[:add] || {}).each_value do |attrs|
603
622
  append_add_check_constraint(table_name, attrs, post_buf)
604
623
  end
624
+
625
+ (delta[:validate] || {}).each_value do |attrs|
626
+ append_validate_check_constraint(table_name, attrs, post_buf)
627
+ end
605
628
  end
606
629
 
607
630
  def append_add_check_constraint(table_name, attrs, buf, force_bulk_change = false)
@@ -628,6 +651,15 @@ remove_check_constraint(#{table_name.inspect}, #{expression.inspect}, **#{attrs_
628
651
  RUBY
629
652
  end
630
653
 
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
+
631
663
  def append_change_exclusion_constraints(table_name, delta, pre_buf, post_buf)
632
664
  (delta[:delete] || {}).each_value do |attrs|
633
665
  append_remove_exclusion_constraint(table_name, attrs, pre_buf)
@@ -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.2'
4
+ VERSION = '3.2.4'
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.2
4
+ version: 3.2.4
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.87.0
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.87.0
255
+ version: 1.88.2
256
256
  - !ruby/object:Gem::Dependency
257
257
  name: rubocop-rake
258
258
  requirement: !ruby/object:Gem::Requirement