database_consistency 3.0.15 → 3.1.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 +4 -4
- data/lib/database_consistency/checkers/association_checkers/association_checker.rb +1 -1
- data/lib/database_consistency/checkers/association_checkers/foreign_key_cascade_checker.rb +2 -2
- data/lib/database_consistency/checkers/association_checkers/foreign_key_checker.rb +1 -1
- data/lib/database_consistency/checkers/association_checkers/foreign_key_type_checker.rb +3 -2
- data/lib/database_consistency/checkers/association_checkers/missing_index_checker.rb +6 -2
- data/lib/database_consistency/checkers/column_checkers/enum_value_checker.rb +3 -3
- data/lib/database_consistency/checkers/column_checkers/missing_index_find_by_checker.rb +2 -2
- data/lib/database_consistency/checkers/column_checkers/primary_key_type_checker.rb +1 -1
- data/lib/database_consistency/checkers/column_checkers/three_state_boolean_checker.rb +1 -1
- data/lib/database_consistency/checkers/index_checkers/redundant_index_checker.rb +1 -1
- data/lib/database_consistency/checkers/index_checkers/redundant_unique_index_checker.rb +1 -1
- data/lib/database_consistency/checkers/model_checkers/view_primary_key_checker.rb +1 -1
- data/lib/database_consistency/checkers/validator_checkers/missing_unique_index_checker.rb +3 -2
- data/lib/database_consistency/checkers/validators_fraction_checkers/column_presence_checker.rb +3 -1
- data/lib/database_consistency/checkers/validators_fraction_checkers/numericality_constraint_checker.rb +3 -3
- data/lib/database_consistency/helper.rb +1 -1
- data/lib/database_consistency/processors/indexes_processor.rb +1 -1
- data/lib/database_consistency/version.rb +1 -1
- data/lib/database_consistency/writers/autofix/helpers/migration.rb +15 -3
- data/lib/database_consistency/writers/autofix/inconsistent_types.rb +24 -2
- data/lib/database_consistency/writers/autofix/templates/inconsistent_types.tt +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c8a45e3403f929db1a3524719b0f955b06ac32c922136f5ee930d2eb2b70bdd0
|
|
4
|
+
data.tar.gz: cc864d1567c17eb3e546321c2d4a1448e2bfc02aec6b4ae25dc7a4f5e6eb5f50
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 25ef86b694af8d420c054b6e1a900803d1c577a5091470d274ca54c9880786a620379ccbdcab408c2f697d94878a53e94bd175e395e77d040c6c245105b2a141
|
|
7
|
+
data.tar.gz: 4cd6ea61d0a4243260b754677d1268d5fb4b207f3d0428c4af2db5d485b6dc8202cd9725aaa96696cd36497e0aaea130ea4a3c8a3fa06675f14b74be678bde84
|
|
@@ -25,7 +25,7 @@ module DatabaseConsistency
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def foreign_key # rubocop:disable Metrics/AbcSize
|
|
28
|
-
model.
|
|
28
|
+
model.connection_pool.with_connection { |conn| conn.foreign_keys(model.table_name) }.find do |foreign_key|
|
|
29
29
|
(Helper.extract_columns(association.foreign_key) - Array.wrap(foreign_key.column)).empty? &&
|
|
30
30
|
foreign_key.to_table == association.klass.table_name
|
|
31
31
|
end
|
|
@@ -67,8 +67,8 @@ module DatabaseConsistency
|
|
|
67
67
|
def foreign_key
|
|
68
68
|
@foreign_key ||=
|
|
69
69
|
association.klass
|
|
70
|
-
.
|
|
71
|
-
.foreign_keys(association.klass.table_name)
|
|
70
|
+
.connection_pool
|
|
71
|
+
.with_connection { |conn| conn.foreign_keys(association.klass.table_name) }
|
|
72
72
|
.find { |fk| (Helper.extract_columns(association.foreign_key) - Array.wrap(fk.column)).empty? }
|
|
73
73
|
end
|
|
74
74
|
|
|
@@ -22,7 +22,7 @@ module DatabaseConsistency
|
|
|
22
22
|
supported? &&
|
|
23
23
|
association.belongs_to? && !association.polymorphic? &&
|
|
24
24
|
same_database? &&
|
|
25
|
-
model.
|
|
25
|
+
model.connection_pool.with_connection { |conn| conn.table_exists?(model.table_name) }
|
|
26
26
|
rescue NameError
|
|
27
27
|
false
|
|
28
28
|
end
|
|
@@ -115,7 +115,7 @@ module DatabaseConsistency
|
|
|
115
115
|
|
|
116
116
|
# @return [DatabaseConsistency::Databases::Factory]
|
|
117
117
|
def database_factory
|
|
118
|
-
@database_factory ||= Databases::Factory.new(association.active_record.
|
|
118
|
+
@database_factory ||= Databases::Factory.new(association.active_record.connection_pool.with_connection(&:adapter_name))
|
|
119
119
|
end
|
|
120
120
|
|
|
121
121
|
# @param [ActiveRecord::Base] model
|
|
@@ -123,7 +123,8 @@ module DatabaseConsistency
|
|
|
123
123
|
#
|
|
124
124
|
# @return [ActiveRecord::ConnectionAdapters::Column]
|
|
125
125
|
def column(model, column_name)
|
|
126
|
-
model.
|
|
126
|
+
model.connection_pool.with_connection { |conn| conn.columns(model.table_name) }
|
|
127
|
+
.find { |column| column.name == column_name } ||
|
|
127
128
|
(raise Errors::MissingField, missing_field_error(model.table_name, column_name))
|
|
128
129
|
end
|
|
129
130
|
|
|
@@ -70,13 +70,17 @@ module DatabaseConsistency
|
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
def unique_index
|
|
73
|
-
@unique_index ||= association.klass.
|
|
73
|
+
@unique_index ||= association.klass.connection_pool
|
|
74
|
+
.with_connection { |conn| conn.indexes(association.klass.table_name) }
|
|
75
|
+
.find do |index|
|
|
74
76
|
index_keys(index) == association_keys && index.unique
|
|
75
77
|
end
|
|
76
78
|
end
|
|
77
79
|
|
|
78
80
|
def index
|
|
79
|
-
@index ||= association.klass.
|
|
81
|
+
@index ||= association.klass.connection_pool
|
|
82
|
+
.with_connection { |conn| conn.indexes(association.klass.table_name) }
|
|
83
|
+
.find do |index|
|
|
80
84
|
index_keys(index, limit: association_keys.size) == association_keys
|
|
81
85
|
end
|
|
82
86
|
end
|
|
@@ -36,8 +36,8 @@ module DatabaseConsistency
|
|
|
36
36
|
|
|
37
37
|
def enum_column_values # rubocop:disable Metrics/AbcSize
|
|
38
38
|
@enum_column_values ||=
|
|
39
|
-
if model.
|
|
40
|
-
_, values = model.
|
|
39
|
+
if model.connection_pool.with_connection(&:enum_types).is_a?(Array)
|
|
40
|
+
_, values = model.connection_pool.with_connection(&:enum_types).find { |(enum, _)| enum == column.sql_type }
|
|
41
41
|
case values
|
|
42
42
|
when Array then values
|
|
43
43
|
else
|
|
@@ -45,7 +45,7 @@ module DatabaseConsistency
|
|
|
45
45
|
end
|
|
46
46
|
else
|
|
47
47
|
# active_record-postgres_enum gem
|
|
48
|
-
model.
|
|
48
|
+
model.connection_pool.with_connection(&:enum_types)[column.sql_type.to_sym]
|
|
49
49
|
end
|
|
50
50
|
end
|
|
51
51
|
|
|
@@ -65,13 +65,13 @@ module DatabaseConsistency
|
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
def indexed?
|
|
68
|
-
model.
|
|
68
|
+
model.connection_pool.with_connection { |conn| conn.indexes(model.table_name) }.any? do |index|
|
|
69
69
|
Helper.extract_index_columns(index.columns).first == column.name.to_s
|
|
70
70
|
end
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
def primary_key_column?
|
|
74
|
-
primary_key = model.
|
|
74
|
+
primary_key = model.connection_pool.with_connection { |conn| conn.primary_key(model.table_name) }
|
|
75
75
|
|
|
76
76
|
column.name.to_s == Array(primary_key).first.to_s
|
|
77
77
|
end
|
|
@@ -14,7 +14,7 @@ module DatabaseConsistency
|
|
|
14
14
|
|
|
15
15
|
# column should be a boolean and table shouldn't be a view
|
|
16
16
|
def preconditions
|
|
17
|
-
column.type == :boolean && model.
|
|
17
|
+
column.type == :boolean && model.connection_pool.with_connection { |conn| conn.table_exists?(model.table_name) }
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def check
|
|
@@ -47,7 +47,7 @@ module DatabaseConsistency
|
|
|
47
47
|
|
|
48
48
|
def covered_by_index
|
|
49
49
|
@covered_by_index ||=
|
|
50
|
-
model.
|
|
50
|
+
model.connection_pool.with_connection { |conn| conn.indexes(model.table_name) }.find do |another_index|
|
|
51
51
|
next if index.name == another_index.name || !Helper.btree_index?(another_index)
|
|
52
52
|
|
|
53
53
|
clause_equals?(another_index) && include_index_as_prefix?(another_index)
|
|
@@ -47,7 +47,7 @@ module DatabaseConsistency
|
|
|
47
47
|
|
|
48
48
|
def covered_by_index
|
|
49
49
|
@covered_by_index ||=
|
|
50
|
-
model.
|
|
50
|
+
model.connection_pool.with_connection { |conn| conn.indexes(model.table_name) }.find do |another_index|
|
|
51
51
|
next if index.name == another_index.name
|
|
52
52
|
|
|
53
53
|
another_index.unique && clause_equals?(another_index) && contain_index?(another_index)
|
|
@@ -9,7 +9,7 @@ module DatabaseConsistency
|
|
|
9
9
|
def preconditions
|
|
10
10
|
ActiveRecord::VERSION::MAJOR >= 5 &&
|
|
11
11
|
!model.abstract_class? &&
|
|
12
|
-
model.
|
|
12
|
+
model.connection_pool.with_connection { |conn| conn.view_exists?(model.table_name) }
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
# Table of possible statuses
|
|
@@ -47,7 +47,8 @@ module DatabaseConsistency
|
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def unique_index
|
|
50
|
-
@unique_index ||= model.
|
|
50
|
+
@unique_index ||= model.connection_pool.with_connection { |conn| conn.indexes(model.table_name) }
|
|
51
|
+
.find { |index| index_matches?(index) }
|
|
51
52
|
end
|
|
52
53
|
|
|
53
54
|
def index_matches?(index)
|
|
@@ -57,7 +58,7 @@ module DatabaseConsistency
|
|
|
57
58
|
end
|
|
58
59
|
|
|
59
60
|
def primary_key_covers_validation?
|
|
60
|
-
primary_key = model.
|
|
61
|
+
primary_key = model.connection_pool.with_connection { |conn| conn.primary_key(model.table_name) }
|
|
61
62
|
|
|
62
63
|
return false if primary_key.blank?
|
|
63
64
|
|
data/lib/database_consistency/checkers/validators_fraction_checkers/column_presence_checker.rb
CHANGED
|
@@ -19,7 +19,9 @@ module DatabaseConsistency
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def preconditions
|
|
22
|
-
(regular_column || association) &&
|
|
22
|
+
(regular_column || association) &&
|
|
23
|
+
model.connection_pool.with_connection { |conn| conn.table_exists?(model.table_name) } &&
|
|
24
|
+
validators.any?
|
|
23
25
|
end
|
|
24
26
|
|
|
25
27
|
def report_template(status, column_name:, error_slug: nil)
|
|
@@ -33,8 +33,8 @@ module DatabaseConsistency
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
def preconditions
|
|
36
|
-
model.
|
|
37
|
-
model.
|
|
36
|
+
model.connection_pool.with_connection { |conn| conn.respond_to?(:check_constraints) } &&
|
|
37
|
+
model.connection_pool.with_connection { |conn| conn.table_exists?(model.table_name) } &&
|
|
38
38
|
column &&
|
|
39
39
|
validators.any?
|
|
40
40
|
end
|
|
@@ -54,7 +54,7 @@ module DatabaseConsistency
|
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
def check_constraints
|
|
57
|
-
@check_constraints ||= model.
|
|
57
|
+
@check_constraints ||= model.connection_pool.with_connection { |conn| conn.check_constraints(model.table_name) }
|
|
58
58
|
rescue StandardError
|
|
59
59
|
[]
|
|
60
60
|
end
|
|
@@ -9,7 +9,7 @@ module DatabaseConsistency
|
|
|
9
9
|
def check # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
10
10
|
Helper.parent_models(configuration).flat_map do |model|
|
|
11
11
|
DebugContext.with(model: model.name) do
|
|
12
|
-
indexes = model.
|
|
12
|
+
indexes = model.connection_pool.with_connection { |conn| conn.indexes(model.table_name) }
|
|
13
13
|
|
|
14
14
|
indexes.flat_map do |index|
|
|
15
15
|
DebugContext.with(index: index.name) do
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'time'
|
|
4
|
+
|
|
3
5
|
module DatabaseConsistency
|
|
4
6
|
module Writers
|
|
5
7
|
module Autofix
|
|
@@ -7,11 +9,21 @@ module DatabaseConsistency
|
|
|
7
9
|
module Migration # :nodoc:
|
|
8
10
|
def migration_path(name)
|
|
9
11
|
last = migration_context.migrations.last
|
|
10
|
-
version = ActiveRecord::Migration.next_migration_number(last&.version.to_i
|
|
12
|
+
version = ActiveRecord::Migration.next_migration_number(next_version_number(last&.version.to_i))
|
|
11
13
|
|
|
12
14
|
"db/migrate/#{version}_#{name.underscore}.rb"
|
|
13
15
|
end
|
|
14
16
|
|
|
17
|
+
# Adds one second to a timestamp version, so a batch of migrations keeps valid times.
|
|
18
|
+
def next_version_number(last_version)
|
|
19
|
+
return last_version + 1 unless last_version.to_s.match?(/\A\d{14}\z/)
|
|
20
|
+
|
|
21
|
+
time = Time.strptime("#{last_version} +0000", '%Y%m%d%H%M%S %z').utc
|
|
22
|
+
(time + 1).strftime('%Y%m%d%H%M%S').to_i
|
|
23
|
+
rescue ArgumentError
|
|
24
|
+
last_version + 1
|
|
25
|
+
end
|
|
26
|
+
|
|
15
27
|
def migration_path_pattern(name)
|
|
16
28
|
"db/migrate/*_#{name.underscore}.rb"
|
|
17
29
|
end
|
|
@@ -28,10 +40,10 @@ module DatabaseConsistency
|
|
|
28
40
|
return ActiveRecord::MigrationContext.new(ActiveRecord::Migrator.migrations_paths)
|
|
29
41
|
end
|
|
30
42
|
|
|
31
|
-
if ActiveRecord::Base.
|
|
43
|
+
if ActiveRecord::Base.connection_pool.with_connection { |conn| conn.respond_to?(:schema_migration) }
|
|
32
44
|
return ActiveRecord::MigrationContext.new(
|
|
33
45
|
ActiveRecord::Migrator.migrations_paths,
|
|
34
|
-
ActiveRecord::Base.
|
|
46
|
+
ActiveRecord::Base.connection_pool.with_connection(&:schema_migration)
|
|
35
47
|
)
|
|
36
48
|
end
|
|
37
49
|
|
|
@@ -8,19 +8,41 @@ module DatabaseConsistency
|
|
|
8
8
|
{
|
|
9
9
|
table_to_change: report.table_to_change,
|
|
10
10
|
type_to_set: report.type_to_set,
|
|
11
|
-
fk_name: report.fk_name
|
|
11
|
+
fk_name: report.fk_name,
|
|
12
|
+
change_columns: change_columns
|
|
12
13
|
}
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
private
|
|
16
17
|
|
|
18
|
+
# Types such as "character varying" have characters that are not valid in a migration name.
|
|
17
19
|
def migration_name
|
|
18
|
-
"change_#{report.table_to_change}_#{
|
|
20
|
+
"change_#{report.table_to_change}_#{fk_names.join('_')}_to_#{types.join('_')}".gsub(/\W+/, '_').chomp('_')
|
|
19
21
|
end
|
|
20
22
|
|
|
21
23
|
def template_path
|
|
22
24
|
File.join(__dir__, 'templates', 'inconsistent_types.tt')
|
|
23
25
|
end
|
|
26
|
+
|
|
27
|
+
# Symbol literals keep names such as "billing.orders" and "character varying" valid Ruby.
|
|
28
|
+
def change_columns
|
|
29
|
+
fk_names.zip(types).reject { |_fk_name, type| type.nil? }.map do |fk_name, type|
|
|
30
|
+
" change_column #{symbol(report.table_to_change)}, #{symbol(fk_name)}, #{symbol(type)}"
|
|
31
|
+
end.join("\n")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def symbol(name)
|
|
35
|
+
name.to_sym.inspect
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Composite keys are joined with "+" and have one type for each column.
|
|
39
|
+
def fk_names
|
|
40
|
+
report.fk_name.split('+')
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def types
|
|
44
|
+
Array(report.type_to_set)
|
|
45
|
+
end
|
|
24
46
|
end
|
|
25
47
|
end
|
|
26
48
|
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: 3.0
|
|
4
|
+
version: 3.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Evgeniy Demin
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|