database_consistency 1.0.0 → 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b074df0ae4a1316afdb9874c811a02189fd664e00b85220730dd0ae985a36c76
4
- data.tar.gz: f870f9f74117a6ea1b5592e7610becc5f854c375ee4a82f9720b287ae9288462
3
+ metadata.gz: 9e4c24aeb235c8283ce761d7b4d1e3511246eae28c761a55b4f8ef180a090b22
4
+ data.tar.gz: d8e3b783eae3e68e88126c7f9d77de062748201652ec46492522eda6af0722bf
5
5
  SHA512:
6
- metadata.gz: 99e298ec2e58c3bfcc5c10a9e294891b14eee23b1a4c9ccbf3e78f5e500480baf1c3ec397d0a19f5af6e83f05dbe9a297e062358eab9699d324b6b06a1255889
7
- data.tar.gz: 7d742b1d5985792e2fa3b3a6d5195ccb91c4b49cdbcbce800de6db29a16ef1045f4bf786b3d88e981d23b573282d4ddb821ede90951890ab45682c16b37ede7b
6
+ metadata.gz: 005c1fa13f5aab3d833c9fd0690907f5df81a0d34ee2dcb5ea19ace51ed41f1438c803d56b95d3573eaa0938b8d9295a04dd5e2dafb6a6ab6ca48c6fcba3af4e
7
+ data.tar.gz: fab16372c3782df28f84aac35b997c31ccbb68d3be8ee0b8442e09061c8a85797da584153fc3de151ff1761227348b17041302260daede249b3b10753a1d610b
@@ -38,10 +38,14 @@ module DatabaseConsistency
38
38
  model.connection.indexes(model.table_name).find do |another_index|
39
39
  next if index.name == another_index.name
40
40
 
41
- include_index_as_prefix?(another_index)
41
+ clause_equals?(another_index) && include_index_as_prefix?(another_index)
42
42
  end
43
43
  end
44
44
 
45
+ def clause_equals?(another_index)
46
+ another_index.where == index.where
47
+ end
48
+
45
49
  def include_index_as_prefix?(another_index)
46
50
  another_index_columns = Helper.extract_index_columns(another_index.columns)
47
51
  index_columns == another_index_columns.first(index_columns.size)
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Checkers
5
+ # This class checks redundant database indexes
6
+ class RedundantUniqueIndexChecker < IndexChecker
7
+ # Message templates
8
+ REDUNDANT_UNIQUE_INDEX = 'index uniqueness is redundant as (%index) covers it'
9
+
10
+ private
11
+
12
+ # We skip check when:
13
+ # - index is not unique
14
+ def preconditions
15
+ index.unique
16
+ end
17
+
18
+ # Table of possible statuses
19
+ # | validation | status |
20
+ # | ---------- | ------ |
21
+ # | provided | ok |
22
+ # | redundant | fail |
23
+ #
24
+ def check
25
+ if covered_by_index
26
+ report_template(:fail, render_message)
27
+ else
28
+ report_template(:ok)
29
+ end
30
+ end
31
+
32
+ def render_message
33
+ REDUNDANT_UNIQUE_INDEX.sub('%index', covered_by_index.name)
34
+ end
35
+
36
+ def covered_by_index
37
+ @covered_by_index ||=
38
+ model.connection.indexes(model.table_name).find do |another_index|
39
+ next if index.name == another_index.name
40
+
41
+ another_index.unique && clause_equals?(another_index) && contain_index?(another_index)
42
+ end
43
+ end
44
+
45
+ def clause_equals?(another_index)
46
+ another_index.where == index.where
47
+ end
48
+
49
+ def contain_index?(another_index)
50
+ another_index_columns = Helper.extract_index_columns(another_index.columns)
51
+ index_columns & another_index_columns == another_index_columns
52
+ end
53
+
54
+ def index_columns
55
+ @index_columns ||= Helper.extract_index_columns(index.columns)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -7,6 +7,7 @@ module DatabaseConsistency
7
7
  WEAK_OPTIONS = %i[allow_nil allow_blank if unless on].freeze
8
8
  # Message templates
9
9
  CONSTRAINT_MISSING = 'column should be required in the database'
10
+ ASSOCIATION_FOREIGN_KEY_CONSTRAINT_MISSING = 'association foreign key column should be required in the database'
10
11
  POSSIBLE_NULL = 'column is required but there is possible null value insert'
11
12
 
12
13
  private
@@ -15,11 +16,9 @@ module DatabaseConsistency
15
16
  validator.kind == :presence
16
17
  end
17
18
 
18
- # We skip check when:
19
- # - there is no presence validators
20
- # - there is no column in the database with given name
19
+ # We skip the check when there are no presence validators
21
20
  def preconditions
22
- validators.any? && column
21
+ validators.any?
23
22
  end
24
23
 
25
24
  # Table of possible statuses
@@ -33,17 +32,39 @@ module DatabaseConsistency
33
32
  can_be_null = column.null
34
33
  has_weak_option = validators.all? { |validator| validator.options.slice(*WEAK_OPTIONS).any? }
35
34
 
36
- if can_be_null == has_weak_option
37
- report_template(:ok)
38
- elsif can_be_null
35
+ return report_template(:ok) if can_be_null == has_weak_option
36
+ return report_template(:fail, POSSIBLE_NULL) unless can_be_null
37
+
38
+ if regular_column
39
39
  report_template(:fail, CONSTRAINT_MISSING)
40
40
  else
41
- report_template(:fail, POSSIBLE_NULL)
41
+ report_template(:fail, ASSOCIATION_FOREIGN_KEY_CONSTRAINT_MISSING)
42
42
  end
43
43
  end
44
44
 
45
45
  def column
46
- @column ||= model.columns.select.find { |field| field.name == attribute.to_s }
46
+ @column ||= regular_column || association_reference_column ||
47
+ (raise Errors::MissingField, "Missing column in #{model.table_name} for #{attribute}")
48
+ end
49
+
50
+ def regular_column
51
+ @regular_column ||= column_for_name(attribute.to_s)
52
+ end
53
+
54
+ def column_for_name(name)
55
+ model.columns.find { |field| field.name == name.to_s }
56
+ end
57
+
58
+ def association_reference_column
59
+ return unless association_reflection
60
+
61
+ column_for_name(association_reflection.foreign_key)
62
+ end
63
+
64
+ def association_reflection
65
+ model
66
+ .reflect_on_all_associations
67
+ .find { |reflection| reflection.belongs_to? && reflection.name == attribute }
47
68
  end
48
69
  end
49
70
  end
@@ -6,7 +6,8 @@ module DatabaseConsistency
6
6
  class IndexesProcessor < BaseProcessor
7
7
  CHECKERS = [
8
8
  Checkers::UniqueIndexChecker,
9
- Checkers::RedundantIndexChecker
9
+ Checkers::RedundantIndexChecker,
10
+ Checkers::RedundantUniqueIndexChecker
10
11
  ].freeze
11
12
 
12
13
  private
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DatabaseConsistency
4
- VERSION = '1.0.0'
4
+ VERSION = '1.1.3'
5
5
  end
@@ -21,9 +21,7 @@ module DatabaseConsistency
21
21
  end
22
22
 
23
23
  def msg(result)
24
- msg = "#{status_text(result)} #{key_text(result)} #{result.message}"
25
- msg += " (checker: #{result.checker_name})" if config.debug?
26
- msg
24
+ "#{result.checker_name} #{status_text(result)} #{key_text(result)} #{result.message}"
27
25
  end
28
26
 
29
27
  private
@@ -36,6 +36,7 @@ require 'database_consistency/checkers/validators_fraction_checkers/column_prese
36
36
  require 'database_consistency/checkers/index_checkers/index_checker'
37
37
  require 'database_consistency/checkers/index_checkers/unique_index_checker'
38
38
  require 'database_consistency/checkers/index_checkers/redundant_index_checker'
39
+ require 'database_consistency/checkers/index_checkers/redundant_unique_index_checker'
39
40
 
40
41
  require 'database_consistency/processors/base_processor'
41
42
  require 'database_consistency/processors/associations_processor'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: database_consistency
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evgeniy Demin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-24 00:00:00.000000000 Z
11
+ date: 2021-10-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -142,6 +142,7 @@ files:
142
142
  - lib/database_consistency/checkers/column_checkers/primary_key_type_checker.rb
143
143
  - lib/database_consistency/checkers/index_checkers/index_checker.rb
144
144
  - lib/database_consistency/checkers/index_checkers/redundant_index_checker.rb
145
+ - lib/database_consistency/checkers/index_checkers/redundant_unique_index_checker.rb
145
146
  - lib/database_consistency/checkers/index_checkers/unique_index_checker.rb
146
147
  - lib/database_consistency/checkers/validator_checkers/belongs_to_presence_checker.rb
147
148
  - lib/database_consistency/checkers/validator_checkers/missing_unique_index_checker.rb