database_consistency 3.0.16 → 3.1.1

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.
Files changed (23) hide show
  1. checksums.yaml +4 -4
  2. data/lib/database_consistency/checkers/association_checkers/association_checker.rb +1 -1
  3. data/lib/database_consistency/checkers/association_checkers/foreign_key_cascade_checker.rb +2 -2
  4. data/lib/database_consistency/checkers/association_checkers/foreign_key_checker.rb +1 -1
  5. data/lib/database_consistency/checkers/association_checkers/foreign_key_type_checker.rb +3 -2
  6. data/lib/database_consistency/checkers/association_checkers/missing_index_checker.rb +18 -5
  7. data/lib/database_consistency/checkers/column_checkers/enum_value_checker.rb +3 -3
  8. data/lib/database_consistency/checkers/column_checkers/missing_index_find_by_checker.rb +2 -2
  9. data/lib/database_consistency/checkers/column_checkers/primary_key_type_checker.rb +1 -1
  10. data/lib/database_consistency/checkers/column_checkers/three_state_boolean_checker.rb +1 -1
  11. data/lib/database_consistency/checkers/index_checkers/redundant_index_checker.rb +1 -1
  12. data/lib/database_consistency/checkers/index_checkers/redundant_unique_index_checker.rb +1 -1
  13. data/lib/database_consistency/checkers/model_checkers/view_primary_key_checker.rb +1 -1
  14. data/lib/database_consistency/checkers/validator_checkers/missing_unique_index_checker.rb +3 -2
  15. data/lib/database_consistency/checkers/validators_fraction_checkers/column_presence_checker.rb +3 -1
  16. data/lib/database_consistency/checkers/validators_fraction_checkers/numericality_constraint_checker.rb +3 -3
  17. data/lib/database_consistency/helper.rb +6 -2
  18. data/lib/database_consistency/processors/indexes_processor.rb +1 -1
  19. data/lib/database_consistency/version.rb +1 -1
  20. data/lib/database_consistency/writers/autofix/helpers/migration.rb +2 -2
  21. data/lib/database_consistency/writers/simple/missing_foreign_key_cascade.rb +9 -2
  22. data/lib/database_consistency/writers/simple_writer.rb +1 -1
  23. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1cb0dbacf9e21959370981f0eaa7bee89164144821af616d59f7b5e90f9a0b31
4
- data.tar.gz: 7f7e86c94cce08b9b1d858d8099006ac19b58aae8b5e91c4e898ed3289928afd
3
+ metadata.gz: dd7ba14920b79a253aa4c1b603f4dde0478780f0a0a0b4342eb6a928b153545e
4
+ data.tar.gz: f655cf5cff2f43b0bef8986fa9617c7891b484f375e34289aaa585b3541f21cb
5
5
  SHA512:
6
- metadata.gz: 4caf4a0b2bfc7d5887dfb5052b2ec79a979e31589fa357a709f77819ee837a2592f858d8332c1543ce783992e4279aa16f1f53079abaf93a619cd63419d19209
7
- data.tar.gz: a3adc09c9b41d1acc40dbbd393430f4db3e07f3aa0d78a5c97854971cd0ad33beaa7b73db012c6360b36adfaf11794df617db89885a2e7981ed547d3a9837563
6
+ metadata.gz: 3f52bb80a9d738cd62d4385027069b46ea1fd6c8f99fc8aeb161d0d5e75dff8e6004fe61abc0f76497d7dee9e7247ba0cd066018d1920ee23063b928363c25a4
7
+ data.tar.gz: 796243796d3e50f1aaa5dd750523be44c1db2c8736cf97720921435026b60283be024253e9972a7f2cedc2ed63ba0bdde3e991dcefb1bb88fe91c9a85a20c282
@@ -25,7 +25,7 @@ module DatabaseConsistency
25
25
  end
26
26
 
27
27
  def foreign_key # rubocop:disable Metrics/AbcSize
28
- model.connection.foreign_keys(model.table_name).find do |foreign_key|
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
- .connection
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.connection.table_exists?(model.table_name)
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.connection.adapter_name)
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.connection.columns(model.table_name).find { |column| column.name == column_name } ||
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
 
@@ -16,11 +16,13 @@ module DatabaseConsistency
16
16
  # - association isn't a `HasOne` or `HasMany`
17
17
  # - association has `through` option
18
18
  # - associated class doesn't exist
19
+ # - associated class's table doesn't exist
19
20
  def preconditions
20
21
  %i[
21
22
  has_one
22
23
  has_many
23
- ].include?(association.macro) && association.through_reflection.nil? && association.klass
24
+ ].include?(association.macro) && association.through_reflection.nil? &&
25
+ association.klass.table_exists?
24
26
  rescue StandardError
25
27
  false
26
28
  end
@@ -39,7 +41,7 @@ module DatabaseConsistency
39
41
  end
40
42
 
41
43
  def check_unique_has_one
42
- if unique_index
44
+ if unique_index || primary_key_columns.sort == association_keys
43
45
  report_template(:ok)
44
46
  else
45
47
  report_template(:fail, error_slug: :has_one_missing_unique_index)
@@ -47,7 +49,7 @@ module DatabaseConsistency
47
49
  end
48
50
 
49
51
  def check_remaining
50
- if index
52
+ if index || primary_key_columns.first(association_keys.size).sort == association_keys
51
53
  report_template(:ok)
52
54
  else
53
55
  report_template(:fail, error_slug: :association_missing_index)
@@ -70,17 +72,28 @@ module DatabaseConsistency
70
72
  end
71
73
 
72
74
  def unique_index
73
- @unique_index ||= association.klass.connection.indexes(association.klass.table_name).find do |index|
75
+ @unique_index ||= association.klass.connection_pool
76
+ .with_connection { |conn| conn.indexes(association.klass.table_name) }
77
+ .find do |index|
74
78
  index_keys(index) == association_keys && index.unique
75
79
  end
76
80
  end
77
81
 
78
82
  def index
79
- @index ||= association.klass.connection.indexes(association.klass.table_name).find do |index|
83
+ @index ||= association.klass.connection_pool
84
+ .with_connection { |conn| conn.indexes(association.klass.table_name) }
85
+ .find do |index|
80
86
  index_keys(index, limit: association_keys.size) == association_keys
81
87
  end
82
88
  end
83
89
 
90
+ # The primary key is an implicit unique index, so it covers the association as well.
91
+ def primary_key_columns
92
+ @primary_key_columns ||= Array(
93
+ association.klass.connection_pool.with_connection { |conn| conn.primary_key(association.klass.table_name) }
94
+ ).map(&:to_s)
95
+ end
96
+
84
97
  def association_keys
85
98
  @association_keys ||= [association.foreign_key, association.type].compact.map(&:to_s).sort
86
99
  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.connection.enum_types.is_a?(Array)
40
- _, values = model.connection.enum_types.find { |(enum, _)| enum == column.sql_type }
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.connection.enum_types[column.sql_type.to_sym]
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.connection.indexes(model.table_name).any? do |index|
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.connection.primary_key(model.table_name)
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
@@ -70,7 +70,7 @@ module DatabaseConsistency
70
70
 
71
71
  # @return [Boolean]
72
72
  def sqlite?
73
- model.connection.adapter_name == SQLITE_ADAPTER_NAME
73
+ model.connection_pool.with_connection(&:adapter_name) == SQLITE_ADAPTER_NAME
74
74
  end
75
75
  end
76
76
  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.connection.table_exists?(model.table_name)
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.connection.indexes(model.table_name).find do |another_index|
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.connection.indexes(model.table_name).find do |another_index|
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.connection.view_exists?(model.table_name)
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.connection.indexes(model.table_name).find { |index| index_matches?(index) }
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.connection.primary_key(model.table_name)
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
 
@@ -19,7 +19,9 @@ module DatabaseConsistency
19
19
  end
20
20
 
21
21
  def preconditions
22
- (regular_column || association) && model.connection.table_exists?(model.table_name) && validators.any?
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.connection.respond_to?(:check_constraints) &&
37
- model.connection.table_exists?(model.table_name) &&
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.connection.check_constraints(model.table_name)
57
+ @check_constraints ||= model.connection_pool.with_connection { |conn| conn.check_constraints(model.table_name) }
58
58
  rescue StandardError
59
59
  []
60
60
  end
@@ -47,7 +47,7 @@ module DatabaseConsistency
47
47
  end
48
48
 
49
49
  def connected?(klass)
50
- klass.connection
50
+ klass.connection_pool.with_connection { true }
51
51
  rescue ActiveRecord::ConnectionNotEstablished
52
52
  puts "#{klass} does not have an active connection, skipping"
53
53
  false
@@ -66,7 +66,11 @@ module DatabaseConsistency
66
66
  def project_klass?(klass)
67
67
  return true unless Module.respond_to?(:const_source_location) && defined?(Bundler)
68
68
 
69
- !Module.const_source_location(klass.to_s).first.to_s.include?(Bundler.bundle_path.to_s)
69
+ source_path = Module.const_source_location(klass.to_s).first.to_s
70
+ bundle_path = Bundler.bundle_path
71
+ bundle_paths = [bundle_path]
72
+ bundle_paths << bundle_path.realpath if bundle_path.exist?
73
+ bundle_paths.none? { |path| source_path.include?(path.to_s) }
70
74
  rescue NameError
71
75
  false
72
76
  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.connection.indexes(model.table_name)
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,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DatabaseConsistency
4
- VERSION = '3.0.16'
4
+ VERSION = '3.1.1'
5
5
  end
@@ -40,10 +40,10 @@ module DatabaseConsistency
40
40
  return ActiveRecord::MigrationContext.new(ActiveRecord::Migrator.migrations_paths)
41
41
  end
42
42
 
43
- if ActiveRecord::Base.connection.respond_to?(:schema_migration)
43
+ if ActiveRecord::Base.connection_pool.with_connection { |conn| conn.respond_to?(:schema_migration) }
44
44
  return ActiveRecord::MigrationContext.new(
45
45
  ActiveRecord::Migrator.migrations_paths,
46
- ActiveRecord::Base.connection.schema_migration
46
+ ActiveRecord::Base.connection_pool.with_connection(&:schema_migration)
47
47
  )
48
48
  end
49
49
 
@@ -7,15 +7,22 @@ module DatabaseConsistency
7
7
  private
8
8
 
9
9
  def template
10
- 'should have a foreign key with on_delete: :%<cascade_option>s in the database'
10
+ 'should have a foreign key %<cascade_option>s in the database'
11
11
  end
12
12
 
13
13
  def attributes
14
14
  {
15
- cascade_option: report.cascade_option
15
+ cascade_option: cascade_option_text
16
16
  }
17
17
  end
18
18
 
19
+ # nil stands for a foreign key without an on_delete option
20
+ def cascade_option_text
21
+ options = report.cascade_option.compact.map { |option| "with on_delete: :#{option}" }
22
+ options << 'without on_delete' if report.cascade_option.include?(nil)
23
+ options.join(' or ')
24
+ end
25
+
19
26
  def unique_attributes
20
27
  {
21
28
  foreign_table: report.foreign_table,
@@ -31,7 +31,7 @@ module DatabaseConsistency
31
31
  klass =
32
32
  if report.error_slug
33
33
  begin
34
- "DatabaseConsistency::Writers::Simple::#{report.error_slug.to_s.classify}".constantize
34
+ "DatabaseConsistency::Writers::Simple::#{report.error_slug.to_s.camelize}".constantize
35
35
  rescue NameError
36
36
  Simple::DefaultMessage
37
37
  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.16
4
+ version: 3.1.1
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-14 00:00:00.000000000 Z
11
+ date: 2026-10-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord