提交 bdd8d589 编写于 作者: F fatkodima

Fix `transaction` reverting for migrations

[fatkodima & David Verhasselt]
上级 65568988
* Fix `transaction` reverting for migrations.
Before: Commands inside a `transaction` in a reverted migration ran uninverted.
Now: This change fixes that by reverting commands inside `transaction` block.
*fatkodima*, *David Verhasselt*
* Raise an error instead of scanning the filesystem root when `fixture_path` is blank. * Raise an error instead of scanning the filesystem root when `fixture_path` is blank.
*Gannon McGibbon*, *Max Albrecht* *Gannon McGibbon*, *Max Albrecht*
......
...@@ -678,15 +678,13 @@ def revert(*migration_classes) ...@@ -678,15 +678,13 @@ def revert(*migration_classes)
if connection.respond_to? :revert if connection.respond_to? :revert
connection.revert { yield } connection.revert { yield }
else else
recorder = CommandRecorder.new(connection) recorder = command_recorder
@connection = recorder @connection = recorder
suppress_messages do suppress_messages do
connection.revert { yield } connection.revert { yield }
end end
@connection = recorder.delegate @connection = recorder.delegate
recorder.commands.each do |cmd, args, block| recorder.replay(self)
send(cmd, *args, &block)
end
end end
end end
end end
...@@ -962,6 +960,10 @@ def execute_block ...@@ -962,6 +960,10 @@ def execute_block
yield yield
end end
end end
def command_recorder
CommandRecorder.new(connection)
end
end end
# MigrationProxy is used to defer loading of the actual migration classes # MigrationProxy is used to defer loading of the actual migration classes
......
...@@ -108,11 +108,17 @@ def change_table(table_name, options = {}) # :nodoc: ...@@ -108,11 +108,17 @@ def change_table(table_name, options = {}) # :nodoc:
yield delegate.update_table_definition(table_name, self) yield delegate.update_table_definition(table_name, self)
end end
def replay(migration)
commands.each do |cmd, args, block|
migration.send(cmd, *args, &block)
end
end
private private
module StraightReversions # :nodoc: module StraightReversions # :nodoc:
private private
{ transaction: :transaction, {
execute_block: :execute_block, execute_block: :execute_block,
create_table: :drop_table, create_table: :drop_table,
create_join_table: :drop_join_table, create_join_table: :drop_join_table,
...@@ -133,6 +139,17 @@ def invert_#{method}(args, &block) # def invert_create_table(args, &block) ...@@ -133,6 +139,17 @@ def invert_#{method}(args, &block) # def invert_create_table(args, &block)
include StraightReversions include StraightReversions
def invert_transaction(args)
sub_recorder = CommandRecorder.new(delegate)
sub_recorder.revert { yield }
invertions_proc = proc {
sub_recorder.replay(self)
}
[:transaction, args, invertions_proc]
end
def invert_drop_table(args, &block) def invert_drop_table(args, &block)
if args.size == 1 && block == nil if args.size == 1 && block == nil
raise ActiveRecord::IrreversibleMigration, "To avoid mistakes, drop_table is only reversible if given options or a block (can be empty)." raise ActiveRecord::IrreversibleMigration, "To avoid mistakes, drop_table is only reversible if given options or a block (can be empty)."
......
...@@ -16,6 +16,21 @@ def self.find(version) ...@@ -16,6 +16,21 @@ def self.find(version)
V6_0 = Current V6_0 = Current
class V5_2 < V6_0 class V5_2 < V6_0
module CommandRecorder
def invert_transaction(args, &block)
[:transaction, args, block]
end
end
private
def command_recorder
recorder = super
class << recorder
prepend CommandRecorder
end
recorder
end
end end
class V5_1 < V5_2 class V5_1 < V5_2
......
...@@ -22,6 +22,14 @@ def change ...@@ -22,6 +22,14 @@ def change
end end
end end
class InvertibleTransactionMigration < InvertibleMigration
def change
transaction do
super
end
end
end
class InvertibleRevertMigration < SilentMigration class InvertibleRevertMigration < SilentMigration
def change def change
revert do revert do
...@@ -271,6 +279,14 @@ def test_migrate_nested_revert_whole_migration ...@@ -271,6 +279,14 @@ def test_migrate_nested_revert_whole_migration
assert_not revert.connection.table_exists?("horses") assert_not revert.connection.table_exists?("horses")
end end
def test_migrate_revert_transaction
migration = InvertibleTransactionMigration.new
migration.migrate :up
assert migration.connection.table_exists?("horses")
migration.migrate :down
assert_not migration.connection.table_exists?("horses")
end
def test_migrate_revert_change_column_default def test_migrate_revert_change_column_default
migration1 = ChangeColumnDefault1.new migration1 = ChangeColumnDefault1.new
migration1.migrate(:up) migration1.migrate(:up)
......
...@@ -360,6 +360,16 @@ def test_invert_remove_foreign_key_is_irreversible_without_to_table ...@@ -360,6 +360,16 @@ def test_invert_remove_foreign_key_is_irreversible_without_to_table
@recorder.inverse_of :remove_foreign_key, [:dogs] @recorder.inverse_of :remove_foreign_key, [:dogs]
end end
end end
def test_invert_transaction_with_irreversible_inside_is_irreversible
assert_raises(ActiveRecord::IrreversibleMigration) do
@recorder.revert do
@recorder.transaction do
@recorder.execute "some sql"
end
end
end
end
end end
end end
end end
...@@ -127,6 +127,20 @@ def test_legacy_migrations_raises_exception_when_inherited ...@@ -127,6 +127,20 @@ def test_legacy_migrations_raises_exception_when_inherited
assert_match(/LegacyMigration < ActiveRecord::Migration\[4\.2\]/, e.message) assert_match(/LegacyMigration < ActiveRecord::Migration\[4\.2\]/, e.message)
end end
def test_legacy_migrations_not_raise_exception_on_reverting_transaction
migration = Class.new(ActiveRecord::Migration[5.2]) {
def change
transaction do
execute "select 1"
end
end
}.new
assert_nothing_raised do
migration.migrate(:down)
end
end
if current_adapter?(:PostgreSQLAdapter) if current_adapter?(:PostgreSQLAdapter)
class Testing < ActiveRecord::Base class Testing < ActiveRecord::Base
end end
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册