database_consistency 0.8.9 → 1.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 88909c88e70df8df3cedb11f197475d9c6dbe3f812de7fdee9bc3e07663f153a
4
- data.tar.gz: 17f08f988d4c92f750836ccce9ce5f5c7c12fbee74fc03dffcbec0215f3942fa
3
+ metadata.gz: b074df0ae4a1316afdb9874c811a02189fd664e00b85220730dd0ae985a36c76
4
+ data.tar.gz: f870f9f74117a6ea1b5592e7610becc5f854c375ee4a82f9720b287ae9288462
5
5
  SHA512:
6
- metadata.gz: 812d9b13a6efa7c7db96d16d77f2770f213c7fb2adf50d407395e8fb9dc61dbc6bd59bbaebe9982e71662441fc6af98d803246d453ab9a8d5051535984279869
7
- data.tar.gz: a69096397d7d422431425dd18ec9f7763bda7f426fb1d5ba8e9eb3a9ebdc94a5f276d06557e979a03d879280e51d3ac6acd0ba3e1cabd28a033917a404aae3ea
6
+ metadata.gz: 99e298ec2e58c3bfcc5c10a9e294891b14eee23b1a4c9ccbf3e78f5e500480baf1c3ec397d0a19f5af6e83f05dbe9a297e062358eab9699d324b6b06a1255889
7
+ data.tar.gz: 7d742b1d5985792e2fa3b3a6d5195ccb91c4b49cdbcbce800de6db29a16ef1045f4bf786b3d88e981d23b573282d4ddb821ede90951890ab45682c16b37ede7b
@@ -33,11 +33,16 @@ require 'database_consistency/checkers/validator_checkers/missing_unique_index_c
33
33
  require 'database_consistency/checkers/validators_fraction_checkers/validators_fraction_checker'
34
34
  require 'database_consistency/checkers/validators_fraction_checkers/column_presence_checker'
35
35
 
36
+ require 'database_consistency/checkers/index_checkers/index_checker'
37
+ require 'database_consistency/checkers/index_checkers/unique_index_checker'
38
+ require 'database_consistency/checkers/index_checkers/redundant_index_checker'
39
+
36
40
  require 'database_consistency/processors/base_processor'
37
41
  require 'database_consistency/processors/associations_processor'
38
42
  require 'database_consistency/processors/validators_processor'
39
43
  require 'database_consistency/processors/columns_processor'
40
44
  require 'database_consistency/processors/validators_fractions_processor'
45
+ require 'database_consistency/processors/indexes_processor'
41
46
 
42
47
  # The root module
43
48
  module DatabaseConsistency
@@ -68,7 +68,7 @@ module DatabaseConsistency
68
68
  def associated_key
69
69
  @associated_key ||= (
70
70
  if belongs_to_association?
71
- association.active_record_primary_key
71
+ association.association_primary_key
72
72
  else
73
73
  association.foreign_key
74
74
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Checkers
5
+ # The base class for table checkers
6
+ class IndexChecker < BaseChecker
7
+ attr_reader :model, :index
8
+
9
+ def initialize(model, index)
10
+ @model = model
11
+ @index = index
12
+ end
13
+
14
+ def column_or_attribute_name
15
+ @column_or_attribute_name ||= index.name.to_s
16
+ end
17
+
18
+ def table_or_model_name
19
+ @table_or_model_name ||= model.name.to_s
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Checkers
5
+ # This class checks redundant database indexes
6
+ class RedundantIndexChecker < IndexChecker
7
+ # Message templates
8
+ REDUNDANT_INDEX = 'index is redundant as (%index) covers it'
9
+
10
+ private
11
+
12
+ # We skip check when:
13
+ # - index is 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_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
+ include_index_as_prefix?(another_index)
42
+ end
43
+ end
44
+
45
+ def include_index_as_prefix?(another_index)
46
+ another_index_columns = Helper.extract_index_columns(another_index.columns)
47
+ index_columns == another_index_columns.first(index_columns.size)
48
+ end
49
+
50
+ def index_columns
51
+ @index_columns ||= Helper.extract_index_columns(index.columns)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Checkers
5
+ # This class checks missing uniqueness validator
6
+ class UniqueIndexChecker < IndexChecker
7
+ # Message templates
8
+ VALIDATOR_MISSING = 'index is unique in the database but do not have uniqueness validator'
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
+ # | missing | fail |
23
+ #
24
+ def check
25
+ if valid?
26
+ report_template(:ok)
27
+ else
28
+ report_template(:fail, VALIDATOR_MISSING)
29
+ end
30
+ end
31
+
32
+ def valid?
33
+ uniqueness_validators = model.validators.select { |validator| validator.kind == :uniqueness }
34
+
35
+ uniqueness_validators.any? do |validator|
36
+ validator.attributes.any? do |attribute|
37
+ sorted_index_columns == Helper.sorted_uniqueness_validator_columns(attribute, validator, model)
38
+ end
39
+ end
40
+ end
41
+
42
+ def sorted_index_columns
43
+ @sorted_index_columns ||= Helper.extract_index_columns(index.columns).sort
44
+ end
45
+ end
46
+ end
47
+ end
@@ -7,7 +7,7 @@ module DatabaseConsistency
7
7
  MISSING_INDEX = 'model should have proper unique index in the database'
8
8
 
9
9
  def column_or_attribute_name
10
- @column_or_attribute_name ||= index_columns.join('+')
10
+ @column_or_attribute_name ||= Helper.uniqueness_validator_columns(attribute, validator, model).join('+')
11
11
  end
12
12
 
13
13
  private
@@ -33,42 +33,12 @@ module DatabaseConsistency
33
33
 
34
34
  def unique_index
35
35
  @unique_index ||= model.connection.indexes(model.table_name).find do |index|
36
- index.unique && extract_index_columns(index.columns).sort == sorted_index_columns
36
+ index.unique && Helper.extract_index_columns(index.columns).sort == sorted_uniqueness_validator_columns
37
37
  end
38
38
  end
39
39
 
40
- # @return [Array<String>]
41
- def extract_index_columns(index_columns)
42
- return index_columns unless index_columns.is_a?(String)
43
-
44
- index_columns.split(',')
45
- .map(&:strip)
46
- .map { |str| str.gsub(/lower\(/i, 'lower(') }
47
- .map { |str| str.gsub(/\(([^)]+)\)::\w+/, '\1') }
48
- .map { |str| str.gsub(/'([^)]+)'::\w+/, '\1') }
49
- end
50
-
51
- def index_columns
52
- @index_columns ||= ([wrapped_attribute_name] + scope_columns).map(&:to_s)
53
- end
54
-
55
- def scope_columns
56
- @scope_columns ||= Array.wrap(validator.options[:scope]).map do |scope_item|
57
- model._reflect_on_association(scope_item)&.foreign_key || scope_item
58
- end
59
- end
60
-
61
- def sorted_index_columns
62
- @sorted_index_columns ||= index_columns.sort
63
- end
64
-
65
- # @return [String]
66
- def wrapped_attribute_name
67
- if validator.options[:case_sensitive].nil? || validator.options[:case_sensitive]
68
- attribute
69
- else
70
- "lower(#{attribute})"
71
- end
40
+ def sorted_uniqueness_validator_columns
41
+ @sorted_uniqueness_validator_columns ||= Helper.sorted_uniqueness_validator_columns(attribute, validator, model)
72
42
  end
73
43
  end
74
44
  end
@@ -34,5 +34,39 @@ module DatabaseConsistency
34
34
 
35
35
  associations
36
36
  end
37
+
38
+ # @return [Array<String>]
39
+ def extract_index_columns(index_columns)
40
+ return index_columns unless index_columns.is_a?(String)
41
+
42
+ index_columns.split(',')
43
+ .map(&:strip)
44
+ .map { |str| str.gsub(/lower\(/i, 'lower(') }
45
+ .map { |str| str.gsub(/\(([^)]+)\)::\w+/, '\1') }
46
+ .map { |str| str.gsub(/'([^)]+)'::\w+/, '\1') }
47
+ end
48
+
49
+ def sorted_uniqueness_validator_columns(attribute, validator, model)
50
+ uniqueness_validator_columns(attribute, validator, model).sort
51
+ end
52
+
53
+ def uniqueness_validator_columns(attribute, validator, model)
54
+ ([wrapped_attribute_name(attribute, validator)] + scope_columns(validator, model)).map(&:to_s)
55
+ end
56
+
57
+ def scope_columns(validator, model)
58
+ Array.wrap(validator.options[:scope]).map do |scope_item|
59
+ model._reflect_on_association(scope_item)&.foreign_key || scope_item
60
+ end
61
+ end
62
+
63
+ # @return [String]
64
+ def wrapped_attribute_name(attribute, validator)
65
+ if validator.options[:case_sensitive].nil? || validator.options[:case_sensitive]
66
+ attribute
67
+ else
68
+ "lower(#{attribute})"
69
+ end
70
+ end
37
71
  end
38
72
  end
@@ -8,7 +8,8 @@ module DatabaseConsistency
8
8
  ColumnsProcessor,
9
9
  ValidatorsProcessor,
10
10
  AssociationsProcessor,
11
- ValidatorsFractionsProcessor
11
+ ValidatorsFractionsProcessor,
12
+ IndexesProcessor
12
13
  ].flat_map do |processor|
13
14
  processor.new(configuration).reports
14
15
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatabaseConsistency
4
+ module Processors
5
+ # The class to process indexes
6
+ class IndexesProcessor < BaseProcessor
7
+ CHECKERS = [
8
+ Checkers::UniqueIndexChecker,
9
+ Checkers::RedundantIndexChecker
10
+ ].freeze
11
+
12
+ private
13
+
14
+ def check # rubocop:disable Metrics/AbcSize
15
+ Helper.parent_models.flat_map do |model|
16
+ next unless configuration.enabled?(model.name.to_s)
17
+
18
+ indexes = ActiveRecord::Base.connection.indexes(model.table_name)
19
+
20
+ indexes.flat_map do |index|
21
+ enabled_checkers.map do |checker_class|
22
+ checker = checker_class.new(model, index)
23
+ checker.report_if_enabled?(configuration)
24
+ end
25
+ end
26
+ end.compact
27
+ end
28
+ end
29
+ end
30
+ end
@@ -5,3 +5,5 @@ ActiveStorage::Attachment:
5
5
  enabled: false
6
6
  ActiveStorage::Blob:
7
7
  enabled: false
8
+ ActiveStorage::VariantRecord:
9
+ enabled: false
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DatabaseConsistency
4
- VERSION = '0.8.9'
4
+ VERSION = '1.0.0'
5
5
  end
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: 0.8.9
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evgeniy Demin
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-16 00:00:00.000000000 Z
11
+ date: 2021-04-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -122,7 +122,7 @@ dependencies:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
124
  version: '1.3'
125
- description:
125
+ description:
126
126
  email:
127
127
  - lawliet.djez@gmail.com
128
128
  executables:
@@ -140,6 +140,9 @@ files:
140
140
  - lib/database_consistency/checkers/column_checkers/length_constraint_checker.rb
141
141
  - lib/database_consistency/checkers/column_checkers/null_constraint_checker.rb
142
142
  - lib/database_consistency/checkers/column_checkers/primary_key_type_checker.rb
143
+ - lib/database_consistency/checkers/index_checkers/index_checker.rb
144
+ - lib/database_consistency/checkers/index_checkers/redundant_index_checker.rb
145
+ - lib/database_consistency/checkers/index_checkers/unique_index_checker.rb
143
146
  - lib/database_consistency/checkers/validator_checkers/belongs_to_presence_checker.rb
144
147
  - lib/database_consistency/checkers/validator_checkers/missing_unique_index_checker.rb
145
148
  - lib/database_consistency/checkers/validator_checkers/validator_checker.rb
@@ -154,6 +157,7 @@ files:
154
157
  - lib/database_consistency/processors/associations_processor.rb
155
158
  - lib/database_consistency/processors/base_processor.rb
156
159
  - lib/database_consistency/processors/columns_processor.rb
160
+ - lib/database_consistency/processors/indexes_processor.rb
157
161
  - lib/database_consistency/processors/validators_fractions_processor.rb
158
162
  - lib/database_consistency/processors/validators_processor.rb
159
163
  - lib/database_consistency/rescue_error.rb
@@ -165,7 +169,7 @@ homepage: https://github.com/djezzzl/database_consistency
165
169
  licenses:
166
170
  - MIT
167
171
  metadata: {}
168
- post_install_message:
172
+ post_install_message:
169
173
  rdoc_options: []
170
174
  require_paths:
171
175
  - lib
@@ -181,7 +185,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
181
185
  version: '0'
182
186
  requirements: []
183
187
  rubygems_version: 3.0.8
184
- signing_key:
188
+ signing_key:
185
189
  specification_version: 4
186
190
  summary: Provide an easy way to check the consistency of the database constraints
187
191
  with the application validations.