1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

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 #43173
Supercedes #43240

Co-authored-by: Ryan Kerr <leboshi@gmail.com>
This commit is contained in:
eileencodes 2021-10-25 08:38:40 -04:00
parent 6d7fdbac50
commit 059d64b874
No known key found for this signature in database
GPG key ID: BA5C575120BBE8DF
6 changed files with 124 additions and 27 deletions

View file

@ -1,3 +1,22 @@
* Add support for setting the filename of the schema or structure dump in the database config.
Applications may now set their the filename or path of the schema / structure dump file in their database configuration.
```yaml
production:
primary:
database: my_db
schema_dump: my_schema_dump_filename.rb
animals:
database: animals_db
schema_dump: false
```
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`.
*Eileen M. Uchitelle*, *Ryan Kerr*
* Add `ActiveRecord::Base.prohibit_shard_swapping` to prevent attempts to change the shard within a block. * Add `ActiveRecord::Base.prohibit_shard_swapping` to prevent attempts to change the shard within a block.
*John Crepezzi*, *Eileen M. Uchitelle* *John Crepezzi*, *Eileen M. Uchitelle*

View file

@ -121,14 +121,39 @@ module ActiveRecord
Base.configurations.primary?(name) Base.configurations.primary?(name)
end end
# Determines whether to dump the schema for a database. # Determines whether to dump the schema/structure files and the
def schema_dump # filename that should be used.
configuration_hash.fetch(:schema_dump, true) #
# If +configuration_hash[:schema_dump]+ is set to +false+ or +nil+
# the schema will not be dumped.
#
# If the config option is set that will be used. Otherwise Rails
# will generate the filename from the database config name.
def schema_dump(format = ActiveRecord.schema_format)
if configuration_hash.key?(:schema_dump)
if config = configuration_hash[:schema_dump]
config
end
elsif primary?
schema_file_type(format)
else
"#{name}_#{schema_file_type(format)}"
end
end end
def database_tasks? # :nodoc: def database_tasks? # :nodoc:
!replica? && !!configuration_hash.fetch(:database_tasks, true) !replica? && !!configuration_hash.fetch(:database_tasks, true)
end end
private
def schema_file_type(format)
case format
when :ruby
"schema.rb"
when :sql
"structure.sql"
end
end
end end
end end
end end

View file

@ -216,10 +216,9 @@ module ActiveRecord
dump_schema(db_config, ActiveRecord.schema_format) dump_schema(db_config, ActiveRecord.schema_format)
end end
rescue ActiveRecord::NoDatabaseError rescue ActiveRecord::NoDatabaseError
config_name = db_config.name create_current(db_config.env_name, db_config.name)
create_current(db_config.env_name, config_name)
if File.exist?(dump_filename(config_name)) if File.exist?(schema_dump_path(db_config))
load_schema( load_schema(
db_config, db_config,
ActiveRecord.schema_format, ActiveRecord.schema_format,
@ -378,7 +377,7 @@ module ActiveRecord
end end
def load_schema(db_config, format = ActiveRecord.schema_format, file = nil) # :nodoc: def load_schema(db_config, format = ActiveRecord.schema_format, file = nil) # :nodoc:
file ||= dump_filename(db_config.name, format) file ||= schema_dump_path(db_config, format)
verbose_was, Migration.verbose = Migration.verbose, verbose? && ENV["VERBOSE"] verbose_was, Migration.verbose = Migration.verbose, verbose? && ENV["VERBOSE"]
check_schema_file(file) check_schema_file(file)
@ -406,9 +405,7 @@ module ActiveRecord
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.") 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.")
end end
name ||= db_config.name file ||= schema_dump_path(db_config)
file ||= dump_filename(name, format)
return true unless File.exist?(file) return true unless File.exist?(file)
@ -421,7 +418,7 @@ module ActiveRecord
end end
def reconstruct_from_schema(db_config, format = ActiveRecord.schema_format, file = nil) # :nodoc: def reconstruct_from_schema(db_config, format = ActiveRecord.schema_format, file = nil) # :nodoc:
file ||= dump_filename(db_config.name, format) file ||= schema_dump_path(db_config, format)
check_schema_file(file) check_schema_file(file)
@ -440,7 +437,7 @@ module ActiveRecord
def dump_schema(db_config, format = ActiveRecord.schema_format) # :nodoc: def dump_schema(db_config, format = ActiveRecord.schema_format) # :nodoc:
require "active_record/schema_dumper" require "active_record/schema_dumper"
filename = dump_filename(db_config.name, format) filename = schema_dump_path(db_config, format)
connection = ActiveRecord::Base.connection connection = ActiveRecord::Base.connection
FileUtils.mkdir_p(db_dir) FileUtils.mkdir_p(db_dir)
@ -475,6 +472,8 @@ module ActiveRecord
end end
def dump_filename(db_config_name, format = ActiveRecord.schema_format) def dump_filename(db_config_name, format = ActiveRecord.schema_format)
ActiveSupport::Deprecation.warn("#dump_filename is deprecated. Please call `schema_dump_path` or call `schema_dump` on the `db_config` directly.")
filename = if ActiveRecord::Base.configurations.primary?(db_config_name) filename = if ActiveRecord::Base.configurations.primary?(db_config_name)
schema_file_type(format) schema_file_type(format)
else else
@ -484,6 +483,19 @@ module ActiveRecord
ENV["SCHEMA"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, filename) ENV["SCHEMA"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, filename)
end end
def schema_dump_path(db_config, format = ActiveRecord.schema_format)
return ENV["SCHEMA"] if ENV["SCHEMA"]
filename = db_config.schema_dump(format)
return unless filename
if File.dirname(filename) == ActiveRecord::Tasks::DatabaseTasks.db_dir
filename
else
File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, filename)
end
end
def cache_dump_filename(db_config_name, schema_cache_path: nil) def cache_dump_filename(db_config_name, schema_cache_path: nil)
filename = if ActiveRecord::Base.configurations.primary?(db_config_name) filename = if ActiveRecord::Base.configurations.primary?(db_config_name)
"schema_cache.yml" "schema_cache.yml"

View file

@ -105,12 +105,12 @@ module ActiveRecord
def test_default_schema_dump_value def test_default_schema_dump_value
config = HashConfig.new("default_env", "primary", {}) config = HashConfig.new("default_env", "primary", {})
assert_equal true, config.schema_dump assert_equal "schema.rb", config.schema_dump
end end
def test_schema_dump_value_set_to_true def test_schema_dump_value_set_to_filename
config = HashConfig.new("default_env", "primary", { schema_dump: true }) config = HashConfig.new("default_env", "primary", { schema_dump: "my_schema.rb" })
assert_equal true, config.schema_dump assert_equal "my_schema.rb", config.schema_dump
end end
def test_schema_dump_value_set_to_nil def test_schema_dump_value_set_to_nil
@ -120,7 +120,7 @@ module ActiveRecord
def test_schema_dump_value_set_to_false def test_schema_dump_value_set_to_false
config = HashConfig.new("default_env", "primary", { schema_dump: false }) config = HashConfig.new("default_env", "primary", { schema_dump: false })
assert_equal false, config.schema_dump assert_nil config.schema_dump
end end
def test_database_tasks_defaults_to_true def test_database_tasks_defaults_to_true

View file

@ -298,7 +298,27 @@ module ActiveRecord
def test_ensure_db_dir def test_ensure_db_dir
Dir.mktmpdir do |dir| Dir.mktmpdir do |dir|
ActiveRecord::Tasks::DatabaseTasks.stub(:db_dir, dir) do ActiveRecord::Tasks::DatabaseTasks.stub(:db_dir, dir) do
db_config = OpenStruct.new(name: "fake_db_config") updated_hash = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary").configuration_hash.merge(schema_dump: "fake_db_config_schema.rb")
db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new("arunit", "primary", updated_hash)
path = "#{dir}/fake_db_config_schema.rb"
FileUtils.rm_rf(dir)
assert_not File.file?(path)
ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config)
assert File.file?(path)
end
end
ensure
ActiveRecord::Base.clear_cache!
end
def test_db_dir_ignored_if_included_in_schema_dump
Dir.mktmpdir do |dir|
ActiveRecord::Tasks::DatabaseTasks.stub(:db_dir, dir) do
updated_hash = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary").configuration_hash.merge(schema_dump: "#{dir}/fake_db_config_schema.rb")
db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new("arunit", "primary", updated_hash)
path = "#{dir}/fake_db_config_schema.rb" path = "#{dir}/fake_db_config_schema.rb"
FileUtils.rm_rf(dir) FileUtils.rm_rf(dir)
@ -1598,7 +1618,17 @@ module ActiveRecord
def test_check_dump_filename_defaults def test_check_dump_filename_defaults
ActiveRecord::Tasks::DatabaseTasks.stub(:db_dir, "/tmp") do ActiveRecord::Tasks::DatabaseTasks.stub(:db_dir, "/tmp") do
with_stubbed_configurations do with_stubbed_configurations do
assert_equal "/tmp/schema.rb", ActiveRecord::Tasks::DatabaseTasks.dump_filename(config_for("development", "primary").name) assert_equal "/tmp/schema.rb", ActiveRecord::Tasks::DatabaseTasks.schema_dump_path(config_for("development", "primary"))
end
end
end
def test_dump_filename_is_deprecated
ActiveRecord::Tasks::DatabaseTasks.stub(:db_dir, "/tmp") do
with_stubbed_configurations do
assert_deprecated do
assert_equal "/tmp/schema.rb", ActiveRecord::Tasks::DatabaseTasks.dump_filename(config_for("development", "primary").name)
end
end end
end end
end end
@ -1608,7 +1638,7 @@ module ActiveRecord
ENV["SCHEMA"] = "schema_path" ENV["SCHEMA"] = "schema_path"
ActiveRecord::Tasks::DatabaseTasks.stub(:db_dir, "/tmp") do ActiveRecord::Tasks::DatabaseTasks.stub(:db_dir, "/tmp") do
with_stubbed_configurations do with_stubbed_configurations do
assert_equal "schema_path", ActiveRecord::Tasks::DatabaseTasks.dump_filename(config_for("development", "primary").name) assert_equal "schema_path", ActiveRecord::Tasks::DatabaseTasks.schema_dump_path(config_for("development", "primary"))
end end
end end
ensure ensure
@ -1619,7 +1649,7 @@ module ActiveRecord
define_method("test_check_dump_filename_for_#{fmt}_format") do define_method("test_check_dump_filename_for_#{fmt}_format") do
ActiveRecord::Tasks::DatabaseTasks.stub(:db_dir, "/tmp") do ActiveRecord::Tasks::DatabaseTasks.stub(:db_dir, "/tmp") do
with_stubbed_configurations do with_stubbed_configurations do
assert_equal "/tmp/#{filename}", ActiveRecord::Tasks::DatabaseTasks.dump_filename(config_for("development", "primary").name, fmt) assert_equal "/tmp/#{filename}", ActiveRecord::Tasks::DatabaseTasks.schema_dump_path(config_for("development", "primary"), fmt)
end end
end end
end end
@ -1631,7 +1661,18 @@ module ActiveRecord
"development" => { "primary" => { "database" => "dev-db" }, "secondary" => { "database" => "secondary-dev-db" } }, "development" => { "primary" => { "database" => "dev-db" }, "secondary" => { "database" => "secondary-dev-db" } },
} }
with_stubbed_configurations(configurations) do with_stubbed_configurations(configurations) do
assert_equal "/tmp/secondary_schema.rb", ActiveRecord::Tasks::DatabaseTasks.dump_filename(config_for("development", "secondary").name) assert_equal "/tmp/secondary_schema.rb", ActiveRecord::Tasks::DatabaseTasks.schema_dump_path(config_for("development", "secondary"))
end
end
end
def test_setting_schema_dump_to_nil
ActiveRecord::Tasks::DatabaseTasks.stub(:db_dir, "/tmp") do
configurations = {
"development" => { "primary" => { "database" => "dev-db", "schema_dump" => false } },
}
with_stubbed_configurations(configurations) do
assert_nil ActiveRecord::Tasks::DatabaseTasks.schema_dump_path(config_for("development", "primary"))
end end
end end
end end
@ -1644,7 +1685,7 @@ module ActiveRecord
"development" => { "primary" => { "database" => "dev-db" }, "secondary" => { "database" => "secondary-dev-db" } }, "development" => { "primary" => { "database" => "dev-db" }, "secondary" => { "database" => "secondary-dev-db" } },
} }
with_stubbed_configurations(configurations) do with_stubbed_configurations(configurations) do
assert_equal "schema_path", ActiveRecord::Tasks::DatabaseTasks.dump_filename(config_for("development", "secondary").name) assert_equal "schema_path", ActiveRecord::Tasks::DatabaseTasks.schema_dump_path(config_for("development", "secondary"))
end end
end end
ensure ensure
@ -1658,7 +1699,7 @@ module ActiveRecord
"development" => { "primary" => { "database" => "dev-db" }, "secondary" => { "database" => "secondary-dev-db" } }, "development" => { "primary" => { "database" => "dev-db" }, "secondary" => { "database" => "secondary-dev-db" } },
} }
with_stubbed_configurations(configurations) do with_stubbed_configurations(configurations) do
assert_equal "/tmp/secondary_#{filename}", ActiveRecord::Tasks::DatabaseTasks.dump_filename(config_for("development", "secondary").name, fmt) assert_equal "/tmp/secondary_#{filename}", ActiveRecord::Tasks::DatabaseTasks.schema_dump_path(config_for("development", "secondary"), fmt)
end end
end end
end end

View file

@ -425,21 +425,21 @@ module ApplicationTests
assert_match(/up\s+002\s+Two migration/, output) assert_match(/up\s+002\s+Two migration/, output)
end end
test "schema generation when dump_schema_after_migration and schema_dump are true" do test "schema generation when dump_schema_after_migration and schema_dump are set" do
add_to_config("config.active_record.dump_schema_after_migration = true") add_to_config("config.active_record.dump_schema_after_migration = true")
app_file "config/database.yml", <<~EOS app_file "config/database.yml", <<~EOS
development: development:
adapter: sqlite3 adapter: sqlite3
database: 'dev_db' database: 'dev_db'
schema_dump: true schema_dump: "schema_file.rb"
EOS EOS
Dir.chdir(app_path) do Dir.chdir(app_path) do
rails "generate", "model", "book", "title:string" rails "generate", "model", "book", "title:string"
rails "db:migrate" rails "db:migrate"
assert File.exist?("db/schema.rb"), "should dump schema when configured to" assert File.exist?("db/schema_file.rb"), "should dump schema when configured to"
end end
end end