diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 279461d892..b29c26ee3d 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -1140,7 +1140,11 @@ module ActiveRecord end def needs_migration? # :nodoc: - (migrations.collect(&:version) - get_all_versions).size > 0 + pending_migration_versions.size > 0 + end + + def pending_migration_versions # :nodoc: + migrations.collect(&:version) - get_all_versions end def migrations # :nodoc: diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 282cb3ca9d..619debb1cf 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -86,14 +86,23 @@ db_namespace = namespace :db do desc "Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)." task migrate: :load_config do - original_db_config = ActiveRecord::Base.connection_db_config - ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config| - ActiveRecord::Base.establish_connection(db_config) + db_configs = ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env) + + if db_configs.size == 1 ActiveRecord::Tasks::DatabaseTasks.migrate + else + original_db_config = ActiveRecord::Base.connection_db_config + mapped_versions = ActiveRecord::Tasks::DatabaseTasks.db_configs_with_versions(db_configs) + + mapped_versions.sort.each do |version, db_config| + ActiveRecord::Base.establish_connection(db_config) + ActiveRecord::Tasks::DatabaseTasks.migrate(version) + end end + db_namespace["_dump"].invoke ensure - ActiveRecord::Base.establish_connection(original_db_config) + ActiveRecord::Base.establish_connection(original_db_config) if original_db_config end # IMPORTANT: This task won't dump the schema if ActiveRecord.dump_schema_after_migration is set to false diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb index 9164a21e5b..068d11079e 100644 --- a/activerecord/lib/active_record/tasks/database_tasks.rb +++ b/activerecord/lib/active_record/tasks/database_tasks.rb @@ -268,13 +268,13 @@ module ActiveRecord end end - def migrate + def migrate(version = nil) check_target_version scope = ENV["SCOPE"] verbose_was, Migration.verbose = Migration.verbose, verbose? - Base.connection.migration_context.migrate(target_version) do |migration| + Base.connection.migration_context.migrate(target_version || version) do |migration| scope.blank? || scope == migration.scope end.tap do |migrations_ran| Migration.write("No migrations ran. (using #{scope} scope)") if scope.present? && migrations_ran.empty? @@ -285,6 +285,23 @@ module ActiveRecord Migration.verbose = verbose_was end + def db_configs_with_versions(db_configs) # :nodoc: + db_configs_with_versions = {} + + db_configs.each do |db_config| + ActiveRecord::Base.establish_connection(db_config) + versions_to_run = ActiveRecord::Base.connection.migration_context.pending_migration_versions + target_version = ActiveRecord::Tasks::DatabaseTasks.target_version + + versions_to_run.each do |version| + next if target_version && target_version != version + db_configs_with_versions[version] = db_config + end + end + + db_configs_with_versions + end + def migrate_status unless ActiveRecord::Base.connection.schema_migration.table_exists? Kernel.abort "Schema migrations table does not exist yet." diff --git a/railties/test/application/rake/multi_dbs_test.rb b/railties/test/application/rake/multi_dbs_test.rb index baff9d0e1f..71efc2bada 100644 --- a/railties/test/application/rake/multi_dbs_test.rb +++ b/railties/test/application/rake/multi_dbs_test.rb @@ -427,6 +427,30 @@ module ApplicationTests end end + test "db:migrate respects timestamp ordering across databases" do + require "#{app_path}/config/environment" + app_file "db/migrate/01_one_migration.rb", <<-MIGRATION + class OneMigration < ActiveRecord::Migration::Current + end + MIGRATION + + app_file "db/animals_migrate/02_two_migration.rb", <<-MIGRATION + class TwoMigration < ActiveRecord::Migration::Current + end + MIGRATION + + app_file "db/migrate/03_three_migration.rb", <<-MIGRATION + class ThreeMigration < ActiveRecord::Migration::Current + end + MIGRATION + + Dir.chdir(app_path) do + output = rails "db:migrate" + entries = output.scan(/^== (\d+).+migrated/).map(&:first).map(&:to_i) + assert_equal [1, 2, 3], entries + end + end + test "db:migrate and db:schema:dump and db:schema:load works on all databases" do db_migrate_and_schema_dump_and_load end