database_consistency 0.8.3 → 0.8.8
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 +6 -0
- data/lib/database_consistency/checkers/association_checkers/foreign_key_type_checker.rb +128 -0
- data/lib/database_consistency/checkers/base_checker.rb +8 -4
- data/lib/database_consistency/checkers/column_checkers/length_constraint_checker.rb +7 -1
- data/lib/database_consistency/databases/factory.rb +27 -0
- data/lib/database_consistency/databases/types/base.rb +22 -0
- data/lib/database_consistency/databases/types/sqlite.rb +22 -0
- data/lib/database_consistency/errors.rb +11 -0
- data/lib/database_consistency/helper.rb +1 -1
- data/lib/database_consistency/processors/associations_processor.rb +2 -1
- data/lib/database_consistency/rescue_error.rb +1 -1
- data/lib/database_consistency/version.rb +1 -1
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 406fd80b86af26702bba071b8dabe3d8f238923bd7ef6d04776b014d2c5c5575
|
4
|
+
data.tar.gz: b30e6bee8c79233090fb8a94557217a6e2fc72e618b577b858083bf0cd3ba71e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 72147fa9f0eccd7ac75216fe673757541048b143ea31fbab435a83e7cb10f62d3e45ae19a5e0c8e609515d3033fa7d868deba863fd8d7ba152f50d254fb3f534
|
7
|
+
data.tar.gz: 6c172f7b74de5face1e235103a2821d41be08f282a840b756caf73f24285e8cf6f7e1f5ecf5e40f26160a8036c947c335edc5338ef3ca274a989d9353220f04d
|
data/lib/database_consistency.rb
CHANGED
@@ -6,14 +6,20 @@ 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'
|
12
13
|
|
14
|
+
require 'database_consistency/databases/factory'
|
15
|
+
require 'database_consistency/databases/types/base'
|
16
|
+
require 'database_consistency/databases/types/sqlite'
|
17
|
+
|
13
18
|
require 'database_consistency/checkers/base_checker'
|
14
19
|
|
15
20
|
require 'database_consistency/checkers/association_checkers/association_checker'
|
16
21
|
require 'database_consistency/checkers/association_checkers/missing_index_checker'
|
22
|
+
require 'database_consistency/checkers/association_checkers/foreign_key_type_checker'
|
17
23
|
|
18
24
|
require 'database_consistency/checkers/column_checkers/column_checker'
|
19
25
|
require 'database_consistency/checkers/column_checkers/null_constraint_checker'
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DatabaseConsistency
|
4
|
+
module Checkers
|
5
|
+
# This class checks if association's foreign key type is the same as associated model's primary key
|
6
|
+
class ForeignKeyTypeChecker < AssociationChecker
|
7
|
+
INCONSISTENT_TYPE = 'associated model key (%a_f) with type (%a_t) mismatches key (%b_f) with type (%b_t)'
|
8
|
+
|
9
|
+
TYPES = {
|
10
|
+
'serial' => 'integer',
|
11
|
+
'integer' => 'integer',
|
12
|
+
'int' => 'integer',
|
13
|
+
'bigserial' => 'bigint',
|
14
|
+
'bigint' => 'bigint',
|
15
|
+
'bigint unsigned' => 'bigint unsigned'
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# We skip check when:
|
21
|
+
# - association is polymorphic association
|
22
|
+
# - association is has_and_belongs_to_many
|
23
|
+
# - association has `through` option
|
24
|
+
# - associated class doesn't exist
|
25
|
+
def preconditions
|
26
|
+
!association.polymorphic? &&
|
27
|
+
association.through_reflection.nil? &&
|
28
|
+
association.klass.present? &&
|
29
|
+
association.macro != :has_and_belongs_to_many
|
30
|
+
rescue NameError
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
# Table of possible statuses
|
35
|
+
# | type | status |
|
36
|
+
# | ------------ | ------ |
|
37
|
+
# | consistent | ok |
|
38
|
+
# | inconsistent | fail |
|
39
|
+
def check
|
40
|
+
if converted_type(base_column) == converted_type(associated_column)
|
41
|
+
report_template(:ok)
|
42
|
+
else
|
43
|
+
report_template(:fail, render_text)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [String]
|
48
|
+
def render_text
|
49
|
+
INCONSISTENT_TYPE
|
50
|
+
.gsub('%a_t', type(associated_column))
|
51
|
+
.gsub('%a_f', associated_key)
|
52
|
+
.gsub('%b_t', type(base_column))
|
53
|
+
.gsub('%b_f', base_key)
|
54
|
+
end
|
55
|
+
|
56
|
+
# @return [String]
|
57
|
+
def base_key
|
58
|
+
@base_key ||= (
|
59
|
+
if belongs_to_association?
|
60
|
+
association.foreign_key
|
61
|
+
else
|
62
|
+
association.active_record_primary_key
|
63
|
+
end
|
64
|
+
).to_s
|
65
|
+
end
|
66
|
+
|
67
|
+
# @return [String]
|
68
|
+
def associated_key
|
69
|
+
@associated_key ||= (
|
70
|
+
if belongs_to_association?
|
71
|
+
association.active_record_primary_key
|
72
|
+
else
|
73
|
+
association.foreign_key
|
74
|
+
end
|
75
|
+
).to_s
|
76
|
+
end
|
77
|
+
|
78
|
+
# @return [ActiveRecord::ConnectionAdapters::Column]
|
79
|
+
def base_column
|
80
|
+
@base_column ||= column(association.active_record, base_key)
|
81
|
+
end
|
82
|
+
|
83
|
+
# @return [ActiveRecord::ConnectionAdapters::Column]
|
84
|
+
def associated_column
|
85
|
+
@associated_column ||= column(association.klass, associated_key)
|
86
|
+
end
|
87
|
+
|
88
|
+
# @return [DatabaseConsistency::Databases::Factory]
|
89
|
+
def database_factory
|
90
|
+
@database_factory ||= Databases::Factory.new(association.active_record.connection.adapter_name)
|
91
|
+
end
|
92
|
+
|
93
|
+
# @param [ActiveRecord::Base] model
|
94
|
+
# @param [String] column_name
|
95
|
+
#
|
96
|
+
# @return [ActiveRecord::ConnectionAdapters::Column]
|
97
|
+
def column(model, 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."
|
106
|
+
end
|
107
|
+
|
108
|
+
# @param [ActiveRecord::ConnectionAdapters::Column] column
|
109
|
+
#
|
110
|
+
# @return [String]
|
111
|
+
def type(column)
|
112
|
+
column.sql_type
|
113
|
+
end
|
114
|
+
|
115
|
+
# @param [ActiveRecord::ConnectionAdapters::Column]
|
116
|
+
#
|
117
|
+
# @return [String]
|
118
|
+
def converted_type(column)
|
119
|
+
database_factory.type(type(column)).convert
|
120
|
+
end
|
121
|
+
|
122
|
+
# @return [Boolean]
|
123
|
+
def belongs_to_association?
|
124
|
+
association.macro == :belongs_to
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -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,
|
@@ -21,8 +21,14 @@ module DatabaseConsistency
|
|
21
21
|
# We skip check when:
|
22
22
|
# - column hasn't limit constraint
|
23
23
|
# - column insn't string nor text
|
24
|
+
# - column is array (PostgreSQL only)
|
24
25
|
def preconditions
|
25
|
-
!column.limit.nil? && %i[string text].include?(column.type)
|
26
|
+
!column.limit.nil? && %i[string text].include?(column.type) && !postgresql_array?
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Boolean] true if it is an array (PostgreSQL only)
|
30
|
+
def postgresql_array?
|
31
|
+
column.respond_to?(:array) && column.array
|
26
32
|
end
|
27
33
|
|
28
34
|
# Table of possible statuses
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DatabaseConsistency
|
4
|
+
module Databases
|
5
|
+
# Factory for database adapters
|
6
|
+
class Factory
|
7
|
+
attr_reader :adapter
|
8
|
+
|
9
|
+
# @param [String] adapter
|
10
|
+
def initialize(adapter)
|
11
|
+
@adapter = adapter
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [DatabaseConsistency::Databases::Types::Base]
|
15
|
+
def type(type)
|
16
|
+
sqlite? ? Types::Sqlite.new(type) : Types::Base.new(type)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# @return [Boolean]
|
22
|
+
def sqlite?
|
23
|
+
adapter == 'SQLite'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DatabaseConsistency
|
4
|
+
module Databases
|
5
|
+
module Types
|
6
|
+
# Base wrapper for database types
|
7
|
+
class Base
|
8
|
+
attr_reader :type
|
9
|
+
|
10
|
+
# @param [String] type
|
11
|
+
def initialize(type)
|
12
|
+
@type = type
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [String]
|
16
|
+
def convert
|
17
|
+
type
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DatabaseConsistency
|
4
|
+
module Databases
|
5
|
+
module Types
|
6
|
+
# Wraps types for SQLite database
|
7
|
+
class Sqlite < Base
|
8
|
+
TYPES = {
|
9
|
+
'bigserial' => 'bigint',
|
10
|
+
'bigint' => 'bigint',
|
11
|
+
'serial' => 'integer',
|
12
|
+
'integer' => 'integer'
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
# @return [String]
|
16
|
+
def convert
|
17
|
+
TYPES[type] || type
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
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
|
|
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.8
|
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-09-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -133,6 +133,7 @@ files:
|
|
133
133
|
- bin/database_consistency
|
134
134
|
- lib/database_consistency.rb
|
135
135
|
- lib/database_consistency/checkers/association_checkers/association_checker.rb
|
136
|
+
- lib/database_consistency/checkers/association_checkers/foreign_key_type_checker.rb
|
136
137
|
- lib/database_consistency/checkers/association_checkers/missing_index_checker.rb
|
137
138
|
- lib/database_consistency/checkers/base_checker.rb
|
138
139
|
- lib/database_consistency/checkers/column_checkers/column_checker.rb
|
@@ -145,6 +146,10 @@ files:
|
|
145
146
|
- lib/database_consistency/checkers/validators_fraction_checkers/column_presence_checker.rb
|
146
147
|
- lib/database_consistency/checkers/validators_fraction_checkers/validators_fraction_checker.rb
|
147
148
|
- lib/database_consistency/configuration.rb
|
149
|
+
- lib/database_consistency/databases/factory.rb
|
150
|
+
- lib/database_consistency/databases/types/base.rb
|
151
|
+
- lib/database_consistency/databases/types/sqlite.rb
|
152
|
+
- lib/database_consistency/errors.rb
|
148
153
|
- lib/database_consistency/helper.rb
|
149
154
|
- lib/database_consistency/processors/associations_processor.rb
|
150
155
|
- lib/database_consistency/processors/base_processor.rb
|
@@ -175,7 +180,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
175
180
|
- !ruby/object:Gem::Version
|
176
181
|
version: '0'
|
177
182
|
requirements: []
|
178
|
-
rubygems_version: 3.
|
183
|
+
rubygems_version: 3.0.8
|
179
184
|
signing_key:
|
180
185
|
specification_version: 4
|
181
186
|
summary: Provide an easy way to check the consistency of the database constraints
|