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.
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 +6 -2
  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 +1 -1
  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 +15 -3
  21. data/lib/database_consistency/writers/autofix/inconsistent_types.rb +24 -2
  22. data/lib/database_consistency/writers/autofix/templates/inconsistent_types.tt +1 -1
  23. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d104523d7460faf27ac7523f032b1802b140560870685207fecaac76bcf76367
4
- data.tar.gz: 955b8cc5a9217e578d4a2a2d36a198eca615b11c0ac7e060382904229950fd5c
3
+ metadata.gz: c8a45e3403f929db1a3524719b0f955b06ac32c922136f5ee930d2eb2b70bdd0
4
+ data.tar.gz: cc864d1567c17eb3e546321c2d4a1448e2bfc02aec6b4ae25dc7a4f5e6eb5f50
5
5
  SHA512:
6
- metadata.gz: 652477c0b3c936abbd3f443fad7de53331339a012d229939e61c204d3e60c03a55c3c5eb741aa5aaf5e4dd8d0ac03865e7a4ed80d58d37f85bf2e885eeaf163e
7
- data.tar.gz: 657642ff01a583796083f8030a42cd0411b641ebe1fa624872f7467506815a15199493e4cc5765d300e58d65257f93a9cb8b8239a13302e46bffc87dc699124c
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.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
 
@@ -70,13 +70,17 @@ module DatabaseConsistency
70
70
  end
71
71
 
72
72
  def unique_index
73
- @unique_index ||= association.klass.connection.indexes(association.klass.table_name).find do |index|
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.connection.indexes(association.klass.table_name).find do |index|
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.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
@@ -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.15'
4
+ VERSION = '3.1.0'
5
5
  end
@@ -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 + 1)
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.connection.respond_to?(:schema_migration)
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.connection.schema_migration
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}_#{report.fk_name}_to_#{report.type_to_set}"
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
@@ -1,5 +1,5 @@
1
1
  class %<migration_name>s < ActiveRecord::Migration[%<migration_version>s]
2
2
  def change
3
- change_column :%<table_to_change>s, :%<fk_name>s, :%<type_to_set>s
3
+ %<change_columns>s
4
4
  end
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: 3.0.15
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-08 00:00:00.000000000 Z
11
+ date: 2026-10-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord