Skip to content

Commit 0bded31

Browse files
authored
Merge pull request rails#53797 from fatkodima/change-structure-dump-versions-ordering
Introduce versions formatter for the schema dumper
2 parents 1d841b1 + 0cc1842 commit 0bded31

File tree

8 files changed

+77
-16
lines changed

8 files changed

+77
-16
lines changed

activerecord/CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
* Introduce versions formatter for the schema dumper.
2+
3+
It is now possible to override how schema dumper formats versions information inside the
4+
`structure.sql` file. Currently, the versions are simply sorted in the decreasing order.
5+
Within large teams, this can potentially cause many merge conflicts near the top of the list.
6+
7+
Now, the custom formatter can be provided with a custom sorting logic (e.g. by hash values
8+
of the versions), which can greatly reduce the number of conflicts.
9+
10+
*fatkodima*
11+
112
* Serialized attributes can now be marked as comparable.
213

314
A not rare issue when working with serialized attributes is that the serialized representation of an object

activerecord/lib/active_record.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,12 @@ def self.permanent_connection_checkout=(value)
403403
singleton_class.attr_accessor :migration_strategy
404404
self.migration_strategy = Migration::DefaultStrategy
405405

406+
##
407+
# :singleton-method: schema_versions_formatter
408+
# Specify the formatter used by schema dumper to format versions information.
409+
singleton_class.attr_accessor :schema_versions_formatter
410+
self.schema_versions_formatter = Migration::DefaultSchemaVersionsFormatter
411+
406412
##
407413
# :singleton-method: dump_schema_after_migration
408414
# Specify whether schema dump should happen at the end of the

activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1353,7 +1353,7 @@ def remove_constraint(table_name, constraint_name) # :nodoc:
13531353
execute schema_creation.accept(at)
13541354
end
13551355

1356-
def dump_schema_information # :nodoc:
1356+
def dump_schema_versions # :nodoc:
13571357
versions = pool.schema_migration.versions
13581358
insert_versions_sql(versions) if versions.any?
13591359
end
@@ -1876,16 +1876,8 @@ def remove_timestamps_for_alter(table_name, **options)
18761876
end
18771877

18781878
def insert_versions_sql(versions)
1879-
sm_table = quote_table_name(pool.schema_migration.table_name)
1880-
1881-
if versions.is_a?(Array)
1882-
sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
1883-
sql << versions.reverse.map { |v| "(#{quote(v)})" }.join(",\n")
1884-
sql << ";"
1885-
sql
1886-
else
1887-
"INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"
1888-
end
1879+
versions_formatter = ActiveRecord.schema_versions_formatter.new(self)
1880+
versions_formatter.format(versions)
18891881
end
18901882

18911883
def data_source_sql(name = nil, type: nil)

activerecord/lib/active_record/migration.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,7 @@ def initialize
571571
class Migration
572572
autoload :CommandRecorder, "active_record/migration/command_recorder"
573573
autoload :Compatibility, "active_record/migration/compatibility"
574+
autoload :DefaultSchemaVersionsFormatter, "active_record/migration/default_schema_versions_formatter"
574575
autoload :JoinTable, "active_record/migration/join_table"
575576
autoload :ExecutionStrategy, "active_record/migration/execution_strategy"
576577
autoload :DefaultStrategy, "active_record/migration/default_strategy"
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# frozen_string_literal: true
2+
3+
module ActiveRecord
4+
class Migration
5+
# This class is used by the schema dumper to format versions information.
6+
#
7+
# The class receives the current +connection+ when initialized.
8+
class DefaultSchemaVersionsFormatter # :nodoc:
9+
def initialize(connection)
10+
@connection = connection
11+
end
12+
13+
def format(versions)
14+
sm_table = connection.quote_table_name(connection.pool.schema_migration.table_name)
15+
16+
if versions.is_a?(Array)
17+
sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
18+
sql << versions.reverse.map { |v| "(#{connection.quote(v)})" }.join(",\n")
19+
sql << ";"
20+
sql
21+
else
22+
"INSERT INTO #{sm_table} (version) VALUES (#{connection.quote(versions)});"
23+
end
24+
end
25+
26+
private
27+
attr_reader :connection
28+
end
29+
end
30+
end

activerecord/lib/active_record/tasks/database_tasks.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ def dump_schema(db_config, format = ActiveRecord.schema_format) # :nodoc:
445445
structure_dump(db_config, filename)
446446
if migration_connection_pool.schema_migration.table_exists?
447447
File.open(filename, "a") do |f|
448-
f.puts migration_connection.dump_schema_information
448+
f.puts migration_connection.dump_schema_versions
449449
f.print "\n"
450450
end
451451
end

activerecord/test/cases/schema_dumper_test.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,19 @@ def standard_dump
1818
@@standard_dump ||= dump_all_table_schema
1919
end
2020

21-
def test_dump_schema_information_with_empty_versions
21+
def test_dump_schema_versions_with_empty_versions
2222
@schema_migration.delete_all_versions
23-
schema_info = ActiveRecord::Base.lease_connection.dump_schema_information
23+
schema_info = ActiveRecord::Base.lease_connection.dump_schema_versions
2424
assert_no_match(/INSERT INTO/, schema_info)
2525
end
2626

27-
def test_dump_schema_information_outputs_lexically_reverse_ordered_versions_regardless_of_database_order
27+
def test_dump_schema_versions_outputs_lexically_reverse_ordered_versions_regardless_of_database_order
2828
versions = %w{ 20100101010101 20100201010101 20100301010101 }
2929
versions.shuffle.each do |v|
3030
@schema_migration.create_version(v)
3131
end
3232

33-
schema_info = ActiveRecord::Base.lease_connection.dump_schema_information
33+
schema_info = ActiveRecord::Base.lease_connection.dump_schema_versions
3434
expected = <<~STR
3535
INSERT INTO #{quote_table_name("schema_migrations")} (version) VALUES
3636
('20100301010101'),

guides/source/configuring.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,6 +1205,27 @@ end
12051205
config.active_record.migration_strategy = CustomMigrationStrategy
12061206
```
12071207
1208+
#### `config.active_record.schema_versions_formatter`
1209+
1210+
Controls the formatter class used by schema dumper to format versions information. Custom class can be provided
1211+
to change the default behavior:
1212+
1213+
```ruby
1214+
class CustomSchemaVersionsFormatter
1215+
def format(versions)
1216+
# Special sorting of versions to reduce the likelihood of conflicts.
1217+
sorted_versions = versions.sort { |a, b| b.to_s.reverse <=> a.to_s.reverse }
1218+
1219+
sql = +"INSERT INTO schema_migrations (version) VALUES\n"
1220+
sql << sorted_versions.map { |v| "(#{connection.quote(v)})" }.join(",\n")
1221+
sql << ";"
1222+
sql
1223+
end
1224+
end
1225+
1226+
config.active_record.schema_versions_formatter = CustomSchemaVersionsFormatter
1227+
```
1228+
12081229
#### `config.active_record.lock_optimistically`
12091230
12101231
Controls whether Active Record will use optimistic locking and is `true` by default.

0 commit comments

Comments
 (0)