Skip to content

Commit 059d64b

Browse files
eileencodesleboshi
andcommitted
Add support for setting the schema/structure dump filepath in the config
Previously Rails would generate your schema/structure dump based on the database config name. As noted on the forum in https://discuss.rubyonrails.org/t/horizontal-sharding-schema-management/78621/6 this is problematic if you want to your shards to share the same schema dump file. This is also useful for applications that might have already written a custom solution for setting the schema dump filename and want to use something upstream. We allow setting the path for the schema cache, why not the schema dump too? To do this I deprecated the old schema_file database task in favor for a new one that passes the `db_config`. The code to derive the filename from the db_config has been duplicated there and will be the correct way to determine filename in the future. Since these values are set on the config they should be accessible in the config as well. I did not need to deprecate the behavior of schema_dump because it has not been included in any release yet (other than the alpha). However the behavior is the same for false/nil. Closes rails#43173 Supercedes rails#43240 Co-authored-by: Ryan Kerr <[email protected]>
1 parent 6d7fdba commit 059d64b

File tree

6 files changed

+124
-27
lines changed

6 files changed

+124
-27
lines changed

activerecord/CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
* Add support for setting the filename of the schema or structure dump in the database config.
2+
3+
Applications may now set their the filename or path of the schema / structure dump file in their database configuration.
4+
5+
6+
```yaml
7+
production:
8+
primary:
9+
database: my_db
10+
schema_dump: my_schema_dump_filename.rb
11+
animals:
12+
database: animals_db
13+
schema_dump: false
14+
```
15+
16+
The filename set in `schema_dump` will be used by the application. If set to `false` the schema will not be dumped. The database tasks are responsible for adding the database directory to the filename. If a full path is provided, the Rails tasks will use that instead of `ActiveRecord::DatabaseTasks.db_dir`.
17+
18+
*Eileen M. Uchitelle*, *Ryan Kerr*
19+
120
* Add `ActiveRecord::Base.prohibit_shard_swapping` to prevent attempts to change the shard within a block.
221

322
*John Crepezzi*, *Eileen M. Uchitelle*

activerecord/lib/active_record/database_configurations/hash_config.rb

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,39 @@ def primary? # :nodoc:
121121
Base.configurations.primary?(name)
122122
end
123123

124-
# Determines whether to dump the schema for a database.
125-
def schema_dump
126-
configuration_hash.fetch(:schema_dump, true)
124+
# Determines whether to dump the schema/structure files and the
125+
# filename that should be used.
126+
#
127+
# If +configuration_hash[:schema_dump]+ is set to +false+ or +nil+
128+
# the schema will not be dumped.
129+
#
130+
# If the config option is set that will be used. Otherwise Rails
131+
# will generate the filename from the database config name.
132+
def schema_dump(format = ActiveRecord.schema_format)
133+
if configuration_hash.key?(:schema_dump)
134+
if config = configuration_hash[:schema_dump]
135+
config
136+
end
137+
elsif primary?
138+
schema_file_type(format)
139+
else
140+
"#{name}_#{schema_file_type(format)}"
141+
end
127142
end
128143

129144
def database_tasks? # :nodoc:
130145
!replica? && !!configuration_hash.fetch(:database_tasks, true)
131146
end
147+
148+
private
149+
def schema_file_type(format)
150+
case format
151+
when :ruby
152+
"schema.rb"
153+
when :sql
154+
"structure.sql"
155+
end
156+
end
132157
end
133158
end
134159
end

activerecord/lib/active_record/tasks/database_tasks.rb

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -216,10 +216,9 @@ def prepare_all
216216
dump_schema(db_config, ActiveRecord.schema_format)
217217
end
218218
rescue ActiveRecord::NoDatabaseError
219-
config_name = db_config.name
220-
create_current(db_config.env_name, config_name)
219+
create_current(db_config.env_name, db_config.name)
221220

222-
if File.exist?(dump_filename(config_name))
221+
if File.exist?(schema_dump_path(db_config))
223222
load_schema(
224223
db_config,
225224
ActiveRecord.schema_format,
@@ -378,7 +377,7 @@ def structure_load(configuration, *arguments)
378377
end
379378

380379
def load_schema(db_config, format = ActiveRecord.schema_format, file = nil) # :nodoc:
381-
file ||= dump_filename(db_config.name, format)
380+
file ||= schema_dump_path(db_config, format)
382381

383382
verbose_was, Migration.verbose = Migration.verbose, verbose? && ENV["VERBOSE"]
384383
check_schema_file(file)
@@ -406,9 +405,7 @@ def schema_up_to_date?(configuration, format = ActiveRecord.schema_format, file
406405
ActiveSupport::Deprecation.warn("`environment` and `name` will be removed as parameters in 7.0.0, you may now pass an ActiveRecord::DatabaseConfigurations::DatabaseConfig as `configuration` instead.")
407406
end
408407

409-
name ||= db_config.name
410-
411-
file ||= dump_filename(name, format)
408+
file ||= schema_dump_path(db_config)
412409

413410
return true unless File.exist?(file)
414411

@@ -421,7 +418,7 @@ def schema_up_to_date?(configuration, format = ActiveRecord.schema_format, file
421418
end
422419

423420
def reconstruct_from_schema(db_config, format = ActiveRecord.schema_format, file = nil) # :nodoc:
424-
file ||= dump_filename(db_config.name, format)
421+
file ||= schema_dump_path(db_config, format)
425422

426423
check_schema_file(file)
427424

@@ -440,7 +437,7 @@ def reconstruct_from_schema(db_config, format = ActiveRecord.schema_format, file
440437

441438
def dump_schema(db_config, format = ActiveRecord.schema_format) # :nodoc:
442439
require "active_record/schema_dumper"
443-
filename = dump_filename(db_config.name, format)
440+
filename = schema_dump_path(db_config, format)
444441
connection = ActiveRecord::Base.connection
445442

446443
FileUtils.mkdir_p(db_dir)
@@ -475,6 +472,8 @@ def schema_file_type(format = ActiveRecord.schema_format)
475472
end
476473

477474
def dump_filename(db_config_name, format = ActiveRecord.schema_format)
475+
ActiveSupport::Deprecation.warn("#dump_filename is deprecated. Please call `schema_dump_path` or call `schema_dump` on the `db_config` directly.")
476+
478477
filename = if ActiveRecord::Base.configurations.primary?(db_config_name)
479478
schema_file_type(format)
480479
else
@@ -484,6 +483,19 @@ def dump_filename(db_config_name, format = ActiveRecord.schema_format)
484483
ENV["SCHEMA"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, filename)
485484
end
486485

486+
def schema_dump_path(db_config, format = ActiveRecord.schema_format)
487+
return ENV["SCHEMA"] if ENV["SCHEMA"]
488+
489+
filename = db_config.schema_dump(format)
490+
return unless filename
491+
492+
if File.dirname(filename) == ActiveRecord::Tasks::DatabaseTasks.db_dir
493+
filename
494+
else
495+
File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, filename)
496+
end
497+
end
498+
487499
def cache_dump_filename(db_config_name, schema_cache_path: nil)
488500
filename = if ActiveRecord::Base.configurations.primary?(db_config_name)
489501
"schema_cache.yml"

activerecord/test/cases/database_configurations/hash_config_test.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,12 @@ def test_idle_timeout_nil_when_less_than_or_equal_to_zero
105105

106106
def test_default_schema_dump_value
107107
config = HashConfig.new("default_env", "primary", {})
108-
assert_equal true, config.schema_dump
108+
assert_equal "schema.rb", config.schema_dump
109109
end
110110

111-
def test_schema_dump_value_set_to_true
112-
config = HashConfig.new("default_env", "primary", { schema_dump: true })
113-
assert_equal true, config.schema_dump
111+
def test_schema_dump_value_set_to_filename
112+
config = HashConfig.new("default_env", "primary", { schema_dump: "my_schema.rb" })
113+
assert_equal "my_schema.rb", config.schema_dump
114114
end
115115

116116
def test_schema_dump_value_set_to_nil
@@ -120,7 +120,7 @@ def test_schema_dump_value_set_to_nil
120120

121121
def test_schema_dump_value_set_to_false
122122
config = HashConfig.new("default_env", "primary", { schema_dump: false })
123-
assert_equal false, config.schema_dump
123+
assert_nil config.schema_dump
124124
end
125125

126126
def test_database_tasks_defaults_to_true

activerecord/test/cases/tasks/database_tasks_test.rb

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,27 @@ class DatabaseTasksDumpSchemaTest < ActiveRecord::TestCase
298298
def test_ensure_db_dir
299299
Dir.mktmpdir do |dir|
300300
ActiveRecord::Tasks::DatabaseTasks.stub(:db_dir, dir) do
301-
db_config = OpenStruct.new(name: "fake_db_config")
301+
updated_hash = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary").configuration_hash.merge(schema_dump: "fake_db_config_schema.rb")
302+
db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new("arunit", "primary", updated_hash)
303+
path = "#{dir}/fake_db_config_schema.rb"
304+
305+
FileUtils.rm_rf(dir)
306+
assert_not File.file?(path)
307+
308+
ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config)
309+
310+
assert File.file?(path)
311+
end
312+
end
313+
ensure
314+
ActiveRecord::Base.clear_cache!
315+
end
316+
317+
def test_db_dir_ignored_if_included_in_schema_dump
318+
Dir.mktmpdir do |dir|
319+
ActiveRecord::Tasks::DatabaseTasks.stub(:db_dir, dir) do
320+
updated_hash = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary").configuration_hash.merge(schema_dump: "#{dir}/fake_db_config_schema.rb")
321+
db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new("arunit", "primary", updated_hash)
302322
path = "#{dir}/fake_db_config_schema.rb"
303323

304324
FileUtils.rm_rf(dir)
@@ -1598,7 +1618,17 @@ def test_check_schema_file_defaults
15981618
def test_check_dump_filename_defaults
15991619
ActiveRecord::Tasks::DatabaseTasks.stub(:db_dir, "/tmp") do
16001620
with_stubbed_configurations do
1601-
assert_equal "/tmp/schema.rb", ActiveRecord::Tasks::DatabaseTasks.dump_filename(config_for("development", "primary").name)
1621+
assert_equal "/tmp/schema.rb", ActiveRecord::Tasks::DatabaseTasks.schema_dump_path(config_for("development", "primary"))
1622+
end
1623+
end
1624+
end
1625+
1626+
def test_dump_filename_is_deprecated
1627+
ActiveRecord::Tasks::DatabaseTasks.stub(:db_dir, "/tmp") do
1628+
with_stubbed_configurations do
1629+
assert_deprecated do
1630+
assert_equal "/tmp/schema.rb", ActiveRecord::Tasks::DatabaseTasks.dump_filename(config_for("development", "primary").name)
1631+
end
16021632
end
16031633
end
16041634
end
@@ -1608,7 +1638,7 @@ def test_check_dump_filename_with_schema_env
16081638
ENV["SCHEMA"] = "schema_path"
16091639
ActiveRecord::Tasks::DatabaseTasks.stub(:db_dir, "/tmp") do
16101640
with_stubbed_configurations do
1611-
assert_equal "schema_path", ActiveRecord::Tasks::DatabaseTasks.dump_filename(config_for("development", "primary").name)
1641+
assert_equal "schema_path", ActiveRecord::Tasks::DatabaseTasks.schema_dump_path(config_for("development", "primary"))
16121642
end
16131643
end
16141644
ensure
@@ -1619,7 +1649,7 @@ def test_check_dump_filename_with_schema_env
16191649
define_method("test_check_dump_filename_for_#{fmt}_format") do
16201650
ActiveRecord::Tasks::DatabaseTasks.stub(:db_dir, "/tmp") do
16211651
with_stubbed_configurations do
1622-
assert_equal "/tmp/#{filename}", ActiveRecord::Tasks::DatabaseTasks.dump_filename(config_for("development", "primary").name, fmt)
1652+
assert_equal "/tmp/#{filename}", ActiveRecord::Tasks::DatabaseTasks.schema_dump_path(config_for("development", "primary"), fmt)
16231653
end
16241654
end
16251655
end
@@ -1631,7 +1661,18 @@ def test_check_dump_filename_defaults_for_non_primary_databases
16311661
"development" => { "primary" => { "database" => "dev-db" }, "secondary" => { "database" => "secondary-dev-db" } },
16321662
}
16331663
with_stubbed_configurations(configurations) do
1634-
assert_equal "/tmp/secondary_schema.rb", ActiveRecord::Tasks::DatabaseTasks.dump_filename(config_for("development", "secondary").name)
1664+
assert_equal "/tmp/secondary_schema.rb", ActiveRecord::Tasks::DatabaseTasks.schema_dump_path(config_for("development", "secondary"))
1665+
end
1666+
end
1667+
end
1668+
1669+
def test_setting_schema_dump_to_nil
1670+
ActiveRecord::Tasks::DatabaseTasks.stub(:db_dir, "/tmp") do
1671+
configurations = {
1672+
"development" => { "primary" => { "database" => "dev-db", "schema_dump" => false } },
1673+
}
1674+
with_stubbed_configurations(configurations) do
1675+
assert_nil ActiveRecord::Tasks::DatabaseTasks.schema_dump_path(config_for("development", "primary"))
16351676
end
16361677
end
16371678
end
@@ -1644,7 +1685,7 @@ def test_check_dump_filename_with_schema_env_with_non_primary_databases
16441685
"development" => { "primary" => { "database" => "dev-db" }, "secondary" => { "database" => "secondary-dev-db" } },
16451686
}
16461687
with_stubbed_configurations(configurations) do
1647-
assert_equal "schema_path", ActiveRecord::Tasks::DatabaseTasks.dump_filename(config_for("development", "secondary").name)
1688+
assert_equal "schema_path", ActiveRecord::Tasks::DatabaseTasks.schema_dump_path(config_for("development", "secondary"))
16481689
end
16491690
end
16501691
ensure
@@ -1658,7 +1699,7 @@ def test_check_dump_filename_with_schema_env_with_non_primary_databases
16581699
"development" => { "primary" => { "database" => "dev-db" }, "secondary" => { "database" => "secondary-dev-db" } },
16591700
}
16601701
with_stubbed_configurations(configurations) do
1661-
assert_equal "/tmp/secondary_#{filename}", ActiveRecord::Tasks::DatabaseTasks.dump_filename(config_for("development", "secondary").name, fmt)
1702+
assert_equal "/tmp/secondary_#{filename}", ActiveRecord::Tasks::DatabaseTasks.schema_dump_path(config_for("development", "secondary"), fmt)
16621703
end
16631704
end
16641705
end

railties/test/application/rake/migrations_test.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -425,21 +425,21 @@ class TwoMigration < ActiveRecord::Migration::Current
425425
assert_match(/up\s+002\s+Two migration/, output)
426426
end
427427

428-
test "schema generation when dump_schema_after_migration and schema_dump are true" do
428+
test "schema generation when dump_schema_after_migration and schema_dump are set" do
429429
add_to_config("config.active_record.dump_schema_after_migration = true")
430430

431431
app_file "config/database.yml", <<~EOS
432432
development:
433433
adapter: sqlite3
434434
database: 'dev_db'
435-
schema_dump: true
435+
schema_dump: "schema_file.rb"
436436
EOS
437437

438438
Dir.chdir(app_path) do
439439
rails "generate", "model", "book", "title:string"
440440
rails "db:migrate"
441441

442-
assert File.exist?("db/schema.rb"), "should dump schema when configured to"
442+
assert File.exist?("db/schema_file.rb"), "should dump schema when configured to"
443443
end
444444
end
445445

0 commit comments

Comments
 (0)