You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I recently stumbled across a weird behavior in ActiveRecord migrations that appears to be a bug: update statements within the change method are being silently executed during the rollback phase when they are neither wrapped in an up_only nor a reversible block. For example:
classResetUsersUpdatedAt < ActiveRecord::Migration[7.1]defchangeupdate(<<~SQL.squish) UPDATE users SET updated_at = NOW() SQLendend
When I run this migration upwards, it behaves as expected. db:migrate yields the following output:
A quick glance at the updated_at column reveals that the update statement was being silently executed in both direction.
My expectation would have been that it would raise an ActiveRecord::IrreversibleMigration just like execute – or otherwise print the executed statement to STDOUT.
Steps to reproduce
Run the code below:
require"bundler/inline"gemfile(true)dosource"https://rubygems.org"gem"rails"gem"sqlite3"endrequire"active_record"require"minitest/autorun"require"logger"ActiveRecord::Base.establish_connection(adapter: "sqlite3",database: ":memory:")ActiveRecord::Base.logger=Logger.new(STDOUT)ActiveRecord::Schema.definedocreate_table:payments,force: truedo |t|
t.text:migration_typeendendclassPayment < ActiveRecord::BaseendclassSetMigrationTypeWithUpdate < ActiveRecord::Migration::Currentdefchangemigration_type=reverting? ? 'down' : 'up'update(<<~SQL.squish) UPDATE payments SET migration_type = "#{migration_type}" SQLendendclassSetMigrationTypeWithExecute < ActiveRecord::Migration::Currentdefchangemigration_type=reverting? ? 'down' : 'up'execute(<<~SQL.squish) UPDATE payments SET migration_type = "#{migration_type}" SQLendendclassBugTest < Minitest::Testdeftest_update_migration_upPayment.create!(migration_type: 'none')SetMigrationTypeWithUpdate.migrate(:up)assert_equal"up",Payment.last!.migration_typeend# This test case is red and yields two unexpected assertions:# * no exception is being raised# * the update statement was executed (the value was changed to "down")## And on top of that, no output to STDOUT was being made so it happened silently..deftest_update_migration_downassert_raisesActiveRecord::IrreversibleMigrationdoSetMigrationTypeWithUpdate.migrate(:down)assert_equal"up",Payment.last!.migration_typeendenddeftest_execute_migration_upPayment.create!(migration_type: 'none')SetMigrationTypeWithExecute.migrate(:up)assert_equal"up",Payment.last!.migration_typeenddeftest_execute_migration_downassert_raisesActiveRecord::IrreversibleMigrationdoSetMigrationTypeWithExecute.migrate(:down)assert_equal"up",Payment.last!.migration_typeendendend
Expected behavior
The test case test_update_migration_down should be green.
If I use update within def change and try to roll back that migration, it should raise an ActiveRecord::IrreversibleMigration exception
Any SQL statement that is being executed during the rollback phase is being printed to the STDOUT
Actual behavior
The test case test_update_migration_down is red
No exception is being made
The rollback phase does not print the "silent" update statement to the STDOUT
System configuration
Rails version: 7.1.3.2 Ruby version: 3.3.0
The text was updated successfully, but these errors were encountered:
makmic
changed the title
ActiveRecord migration: "update" statements are silently being executed in both directions
[ActiveRecord] migrations: update statements are silently being executed in both directions
Apr 16, 2024
Thank you for looking into this issue. I did check if they are affected from the same bug, but it might be worth testing similar statements like insert and delete as part of the fix as well.
Yeah, that is what I was trying to avoid with my patch. Because new methods could be added an we forget to handle them. But my patch broke way more than I expected, so I just reverted it for now. Failing to find a way to make sure all commands are recorded, we will have to explicitly list all commands that can't be reverted.
I recently stumbled across a weird behavior in ActiveRecord migrations that appears to be a bug:
update
statements within thechange
method are being silently executed during the rollback phase when they are neither wrapped in anup_only
nor areversible
block. For example:When I run this migration upwards, it behaves as expected.
db:migrate
yields the following output:However, the output of
db:rollback
is empty:A quick glance at the
updated_at
column reveals that theupdate
statement was being silently executed in both direction.My expectation would have been that it would raise an
ActiveRecord::IrreversibleMigration
just likeexecute
– or otherwise print the executed statement to STDOUT.Steps to reproduce
Run the code below:
Expected behavior
test_update_migration_down
should be green.update
withindef change
and try to roll back that migration, it should raise anActiveRecord::IrreversibleMigration
exceptionActual behavior
test_update_migration_down
is redSystem configuration
Rails version: 7.1.3.2
Ruby version: 3.3.0
The text was updated successfully, but these errors were encountered: