This project is archived and is in readonly mode.
rake db:migrate doesn't detect new migration
Experienced with Rails / ActiveRecord 2.1.1
- You create a first version with (for example) ruby script\generate scaffold product title:string description:text image_url:string
- This create (for example) a migration file called 20080910122415_create_products.rb
- You apply the migration with rake db:migrate
- Now, you add a field to the product table with ruby script\generate migration add_price_to_product price:decimal
- This create a migration file called 20080910125745_add_price_to_product.rb
- If you try to run rake db:migrate, it will actually revert the first migration, not apply the next one! So your product table will get destroyed!
- But if you ran rake alone, it would have told you that one migration was pending
Pls note that applying rake db:migrate (once the table has been destroyed) will apply all migrations in order.
The only workaround I found is to specify the version of the new migration as in: rake db:migrate version=20080910125745
So I'm wondering: is this an expected new behavior?
Comments and changes to this ticket
@Tom : as I explain in the stackoverflow post this is still an error.
at least on windows, and most likely on *nix envs too, when the environment already has a VERSION variable set (and on windows it is case insensitive so Version brings it up too) and VERSION contains some arbitrary string which does not map to an integer, the rake db:migrate command's behaviour is erroneous.
running it once on an empty database will migrate up, but then running that same command again to solve pending migrations will actually revert the database instead of upgrading it.
main incriminated files :
@@@ruby desc "Migrate the database through scripts in db/migrate. Target specific version with VERSION=x. Turn off output with VERBOSE=false." task :migrate => :environment do ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true ActiveRecord::Migrator.migrate("db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil) Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby end
### migration.rb in active records library @@@ruby class Migrator#:nodoc: class << self def migrate(migrations_path, target_version = nil) case when target_version.nil? then up(migrations_path, target_version) when current_version > target_version then down(migrations_path, target_version) else up(migrations_path, target_version) end end
the bug is in this line @@@ruby ActiveRecord::Migrator.migrate("db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
ENV["VERSION"].to_i will return 0 if ENV["VERSION"] is not a valid integer (ruby behaviour). Thus if for some reason the user has a version variable set, it will behave erratically. # Solutions # ## The minimal solution ## Is to fail loudly when VERSION is not an integer, so that at least the user knows what is wrong and doesn't loose data. for this the code should be changed to be @@@ruby ActiveRecord::Migrator.migrate("db/migrate/", ENV["VERSION"] ? Integer.new(ENV["VERSION"]) : nil)
A slightly better solution
at least check that the VERSION is a valid timestamp with respect to the YYYYMMDD format instead of checking for an integer (there may be backward compatibility reasons why you wouldn't want to do that)
Actually list the content of the migration directory and ensure that the VERSION is valid for the files in that directory.
The correct solution
Do not use environment variables to store program argument ... this is just wrong and you can not make any assumptions as to the existing or non existing environment variables.