Skip to content

Commit e15b46c

Browse files
authored
Skip partial indexes and conditional validators in uniqueness checkers (#277)
1 parent 161d5bd commit e15b46c

File tree

7 files changed

+82
-2
lines changed

7 files changed

+82
-2
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
/pkg/
77
/spec/reports/
88
/tmp/
9+
/vendor/bundle/
910

1011
# rspec failure tracking
1112
.rspec_status

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
### [Unreleased]
4+
5+
- Fix `UniqueIndexChecker` to skip partial indexes (indexes with a `WHERE` clause).
6+
- Fix `MissingUniqueIndexChecker` to skip uniqueness validators with a `conditions` option.
7+
38
### [2.1.3] - 2026/02/20
49

510
- Fix `MissingDependentDestroyChecker` to support composite keys. Thanks [Andy Allan](https://github.com/gravitystorm) for reporting this!

lib/database_consistency/checkers/index_checkers/unique_index_checker.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ class UniqueIndexChecker < IndexChecker
88

99
# We skip check when:
1010
# - index is not unique
11+
# - index is a partial index (has a where clause)
1112
def preconditions
12-
index.unique
13+
index.unique && index.where.nil?
1314
end
1415

1516
# Table of possible statuses

lib/database_consistency/checkers/validator_checkers/missing_unique_index_checker.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ def column_or_attribute_name
1818

1919
# We skip check when:
2020
# - validator is not a uniqueness validator
21+
# - validator has a conditions option (partial validation, may correspond to partial index)
2122
def preconditions
22-
validator.kind == :uniqueness
23+
validator.kind == :uniqueness && validator.options[:conditions].nil?
2324
end
2425

2526
# Table of possible statuses

spec/checkers/missing_unique_index_checker_spec.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,4 +272,24 @@
272272
end
273273
end
274274
end
275+
276+
context 'when uniqueness validation has conditions option' do
277+
let(:attribute) { :account_id }
278+
let(:klass) do
279+
define_class do |klass|
280+
klass.validates :account_id, uniqueness: { conditions: -> { where(is_default: true) } }
281+
end
282+
end
283+
284+
before do
285+
define_database_with_entity do |table|
286+
table.integer :account_id
287+
table.boolean :is_default
288+
end
289+
end
290+
291+
specify do
292+
expect(checker.report).to be_nil
293+
end
294+
end
275295
end
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe DatabaseConsistency::Checkers::UniqueIndexChecker, :postgresql do
4+
subject(:checker) { described_class.new(model, index) }
5+
6+
let(:model) { klass }
7+
let(:index) { ActiveRecord::Base.connection.indexes(klass.table_name).first }
8+
9+
let(:index_name) { 'index_name' }
10+
11+
context 'when unique partial index exists' do
12+
before do
13+
define_database_with_entity do |table|
14+
table.integer :account_id
15+
table.boolean :is_default
16+
table.index %i[account_id], unique: true, name: index_name, where: 'is_default = true'
17+
end
18+
end
19+
20+
let(:klass) { define_class }
21+
22+
specify do
23+
expect(checker.report).to be_nil
24+
end
25+
end
26+
end
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe DatabaseConsistency::Checkers::UniqueIndexChecker, :sqlite do
4+
subject(:checker) { described_class.new(model, index) }
5+
6+
let(:model) { klass }
7+
let(:index) { ActiveRecord::Base.connection.indexes(klass.table_name).first }
8+
9+
let(:index_name) { 'index_name' }
10+
11+
context 'when unique partial index exists' do
12+
before do
13+
define_database_with_entity do |table|
14+
table.integer :account_id
15+
table.boolean :is_default
16+
table.index %i[account_id], unique: true, name: index_name, where: 'is_default = 1'
17+
end
18+
end
19+
20+
let(:klass) { define_class }
21+
22+
specify do
23+
expect(checker.report).to be_nil
24+
end
25+
end
26+
end

0 commit comments

Comments
 (0)