database_consistency 0.8.7 → 0.8.12
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 +4 -4
- data/lib/database_consistency.rb +5 -0
- data/lib/database_consistency/checkers/association_checkers/foreign_key_type_checker.rb +14 -3
- data/lib/database_consistency/checkers/base_checker.rb +8 -4
- data/lib/database_consistency/checkers/index_checkers/index_checker.rb +23 -0
- data/lib/database_consistency/checkers/index_checkers/unique_index_checker.rb +47 -0
- data/lib/database_consistency/checkers/validator_checkers/missing_unique_index_checker.rb +4 -33
- data/lib/database_consistency/errors.rb +11 -0
- data/lib/database_consistency/helper.rb +35 -1
- data/lib/database_consistency/processors/base_processor.rb +2 -1
- data/lib/database_consistency/processors/indexes_processor.rb +29 -0
- data/lib/database_consistency/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba8c6b25dd4a98dcc7de0dfad22a1c33e4c3a325ab999aedd037fe743c108bdd
|
4
|
+
data.tar.gz: 9c563d73dec087a460e10b9acedc365bbc45059028df5571d59ec6dbfa11f1b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f51d16f3806889640ad04fca4cddda635c708063d0c6554a6f0589678ca8cfe290db7a9709df5232fe0bbfb14fd8532b8498bb6204e2a341473dd27ae3c40400
|
7
|
+
data.tar.gz: '09097fa4bfec79f64a87df086601253594ce409b4d11b736df0127b3b9af10145f721d761ab90c8a0865cd12c48ecb9429211299d1244e02ddf2a88e11c971a9'
|
data/lib/database_consistency.rb
CHANGED
@@ -6,6 +6,7 @@ require 'database_consistency/version'
|
|
6
6
|
require 'database_consistency/helper'
|
7
7
|
require 'database_consistency/configuration'
|
8
8
|
require 'database_consistency/rescue_error'
|
9
|
+
require 'database_consistency/errors'
|
9
10
|
|
10
11
|
require 'database_consistency/writers/base_writer'
|
11
12
|
require 'database_consistency/writers/simple_writer'
|
@@ -32,11 +33,15 @@ require 'database_consistency/checkers/validator_checkers/missing_unique_index_c
|
|
32
33
|
require 'database_consistency/checkers/validators_fraction_checkers/validators_fraction_checker'
|
33
34
|
require 'database_consistency/checkers/validators_fraction_checkers/column_presence_checker'
|
34
35
|
|
36
|
+
require 'database_consistency/checkers/index_checkers/index_checker'
|
37
|
+
require 'database_consistency/checkers/index_checkers/unique_index_checker'
|
38
|
+
|
35
39
|
require 'database_consistency/processors/base_processor'
|
36
40
|
require 'database_consistency/processors/associations_processor'
|
37
41
|
require 'database_consistency/processors/validators_processor'
|
38
42
|
require 'database_consistency/processors/columns_processor'
|
39
43
|
require 'database_consistency/processors/validators_fractions_processor'
|
44
|
+
require 'database_consistency/processors/indexes_processor'
|
40
45
|
|
41
46
|
# The root module
|
42
47
|
module DatabaseConsistency
|
@@ -19,10 +19,14 @@ module DatabaseConsistency
|
|
19
19
|
|
20
20
|
# We skip check when:
|
21
21
|
# - association is polymorphic association
|
22
|
+
# - association is has_and_belongs_to_many
|
22
23
|
# - association has `through` option
|
23
24
|
# - associated class doesn't exist
|
24
25
|
def preconditions
|
25
|
-
!association.polymorphic? &&
|
26
|
+
!association.polymorphic? &&
|
27
|
+
association.through_reflection.nil? &&
|
28
|
+
association.klass.present? &&
|
29
|
+
association.macro != :has_and_belongs_to_many
|
26
30
|
rescue NameError
|
27
31
|
false
|
28
32
|
end
|
@@ -64,7 +68,7 @@ module DatabaseConsistency
|
|
64
68
|
def associated_key
|
65
69
|
@associated_key ||= (
|
66
70
|
if belongs_to_association?
|
67
|
-
association.
|
71
|
+
association.association_primary_key
|
68
72
|
else
|
69
73
|
association.foreign_key
|
70
74
|
end
|
@@ -91,7 +95,14 @@ module DatabaseConsistency
|
|
91
95
|
#
|
92
96
|
# @return [ActiveRecord::ConnectionAdapters::Column]
|
93
97
|
def column(model, column_name)
|
94
|
-
model.connection.columns(model.table_name).find { |column| column.name == column_name }
|
98
|
+
model.connection.columns(model.table_name).find { |column| column.name == column_name } ||
|
99
|
+
(raise Errors::MissingField, missing_field_error(model.table_name, column_name))
|
100
|
+
end
|
101
|
+
|
102
|
+
# @return [String]
|
103
|
+
def missing_field_error(table_name, column_name)
|
104
|
+
"Association (#{association.name}) of class (#{association.active_record}) relies on "\
|
105
|
+
"field (#{column_name}) of table (#{table_name}) but it is missing."
|
95
106
|
end
|
96
107
|
|
97
108
|
# @param [ActiveRecord::ConnectionAdapters::Column] column
|
@@ -16,16 +16,20 @@ module DatabaseConsistency
|
|
16
16
|
@checker_name ||= name.split('::').last
|
17
17
|
end
|
18
18
|
|
19
|
-
# @
|
20
|
-
|
19
|
+
# @param [Boolean] catch_errors
|
20
|
+
#
|
21
|
+
# @return [Hash, File, nil]
|
22
|
+
def report(catch_errors = true)
|
21
23
|
return unless preconditions
|
22
24
|
|
23
25
|
@report ||= check
|
24
26
|
rescue StandardError => e
|
27
|
+
raise e unless catch_errors
|
28
|
+
|
25
29
|
RescueError.call(e)
|
26
30
|
end
|
27
31
|
|
28
|
-
# @return [Hash, nil]
|
32
|
+
# @return [Hash, File, nil]
|
29
33
|
def report_if_enabled?(configuration)
|
30
34
|
report if enabled?(configuration)
|
31
35
|
end
|
@@ -62,7 +66,7 @@ module DatabaseConsistency
|
|
62
66
|
raise NotImplementedError
|
63
67
|
end
|
64
68
|
|
65
|
-
# @return [
|
69
|
+
# @return [OpenStruct]
|
66
70
|
def report_template(status, message = nil)
|
67
71
|
OpenStruct.new(
|
68
72
|
checker_name: checker_name,
|
@@ -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,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 ||=
|
10
|
+
@column_or_attribute_name ||= Helper.uniqueness_validator_columns(attribute, validator, model).join('+')
|
11
11
|
end
|
12
12
|
|
13
13
|
private
|
@@ -33,41 +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 ==
|
36
|
+
index.unique && Helper.extract_index_columns(index.columns).sort == sorted_uniqueness_validator_columns
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
|
41
|
-
|
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
|
-
end
|
49
|
-
|
50
|
-
def index_columns
|
51
|
-
@index_columns ||= ([wrapped_attribute_name] + scope_columns).map(&:to_s)
|
52
|
-
end
|
53
|
-
|
54
|
-
def scope_columns
|
55
|
-
@scope_columns ||= Array.wrap(validator.options[:scope]).map do |scope_item|
|
56
|
-
model._reflect_on_association(scope_item)&.foreign_key || scope_item
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def sorted_index_columns
|
61
|
-
@sorted_index_columns ||= index_columns.sort
|
62
|
-
end
|
63
|
-
|
64
|
-
# @return [String]
|
65
|
-
def wrapped_attribute_name
|
66
|
-
if validator.options[:case_sensitive].nil? || validator.options[:case_sensitive]
|
67
|
-
attribute
|
68
|
-
else
|
69
|
-
"lower(#{attribute})"
|
70
|
-
end
|
40
|
+
def sorted_uniqueness_validator_columns
|
41
|
+
@sorted_uniqueness_validator_columns ||= Helper.sorted_uniqueness_validator_columns(attribute, validator, model)
|
71
42
|
end
|
72
43
|
end
|
73
44
|
end
|
@@ -8,7 +8,7 @@ module DatabaseConsistency
|
|
8
8
|
# Returns list of models to check
|
9
9
|
def models
|
10
10
|
ActiveRecord::Base.descendants.delete_if(&:abstract_class?).delete_if do |klass|
|
11
|
-
!klass.connection.table_exists?(klass.table_name)
|
11
|
+
!klass.connection.table_exists?(klass.table_name) || klass.name.include?('HABTM_')
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
@@ -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
|
@@ -0,0 +1,29 @@
|
|
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
|
+
].freeze
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def check # rubocop:disable Metrics/AbcSize
|
14
|
+
Helper.parent_models.flat_map do |model|
|
15
|
+
next unless configuration.enabled?(model.name.to_s)
|
16
|
+
|
17
|
+
indexes = ActiveRecord::Base.connection.indexes(model.table_name)
|
18
|
+
|
19
|
+
indexes.flat_map do |index|
|
20
|
+
enabled_checkers.map do |checker_class|
|
21
|
+
checker = checker_class.new(model, index)
|
22
|
+
checker.report_if_enabled?(configuration)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end.compact
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
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.
|
4
|
+
version: 0.8.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Evgeniy Demin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-12-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -140,6 +140,8 @@ 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/unique_index_checker.rb
|
143
145
|
- lib/database_consistency/checkers/validator_checkers/belongs_to_presence_checker.rb
|
144
146
|
- lib/database_consistency/checkers/validator_checkers/missing_unique_index_checker.rb
|
145
147
|
- lib/database_consistency/checkers/validator_checkers/validator_checker.rb
|
@@ -149,10 +151,12 @@ files:
|
|
149
151
|
- lib/database_consistency/databases/factory.rb
|
150
152
|
- lib/database_consistency/databases/types/base.rb
|
151
153
|
- lib/database_consistency/databases/types/sqlite.rb
|
154
|
+
- lib/database_consistency/errors.rb
|
152
155
|
- lib/database_consistency/helper.rb
|
153
156
|
- lib/database_consistency/processors/associations_processor.rb
|
154
157
|
- lib/database_consistency/processors/base_processor.rb
|
155
158
|
- lib/database_consistency/processors/columns_processor.rb
|
159
|
+
- lib/database_consistency/processors/indexes_processor.rb
|
156
160
|
- lib/database_consistency/processors/validators_fractions_processor.rb
|
157
161
|
- lib/database_consistency/processors/validators_processor.rb
|
158
162
|
- lib/database_consistency/rescue_error.rb
|