0
@@ -123,7 +123,8 @@ module ActiveRecord
0
# To run migrations against the currently configured database, use
0
# <tt>rake db:migrate</tt>. This will update the database by running all of the
0
- # pending migrations, creating the <tt>schema_info</tt> table if missing.
0
+ # pending migrations, creating the <tt>schema_migrations</tt> table
0
+ # (see "About the schema_migrations table" section below) if missing.
0
# To roll the database back to a previous migration version, use
0
# <tt>rake db:migrate VERSION=X</tt> where <tt>X</tt> is the version to which
0
@@ -216,6 +217,21 @@ module ActiveRecord
0
# The phrase "Updating salaries..." would then be printed, along with the
0
# benchmark for the block when the block completes.
0
+ # == About the schema_migrations table
0
+ # Rails versions 2.0 and prior used to create a table called
0
+ # <tt>schema_info</tt> when using migrations. This table contained the
0
+ # version of the schema as of the last applied migration.
0
+ # Starting with Rails 2.1, the <tt>schema_info</tt> table is
0
+ # (automatically) replaced by the <tt>schema_migrations</tt> table, which
0
+ # contains the version numbers of all the migrations applied.
0
+ # As a result, it is now possible to add migration files that are numbered
0
+ # lower than the current schema version: when migrating up, those
0
+ # never-applied "interleaved" migrations will be automatically applied, and
0
+ # when migrating down, never-applied "interleaved" migrations will be skipped.
0
cattr_accessor :verbose
0
@@ -315,15 +331,12 @@ module ActiveRecord
0
def migrate(migrations_path, target_version = nil)
0
- when target_version.nil?, current_version < target_version
0
- up(migrations_path, target_version)
0
- when current_version > target_version
0
- down(migrations_path, target_version)
0
- when current_version == target_version
0
- return # You're on the right version
0
+ when target_version.nil? then up(migrations_path, target_version)
0
+ when current_version > target_version then down(migrations_path, target_version)
0
+ else up(migrations_path, target_version)
0
def rollback(migrations_path, steps=1)
0
migrator = self.new(:down, migrations_path)
0
start_index = migrator.migrations.index(migrator.current_migration)
0
@@ -346,12 +359,13 @@ module ActiveRecord
0
self.new(direction, migrations_path, target_version).run
0
- def schema_info_table_name
0
- Base.table_name_prefix + "schema_info" + Base.table_name_suffix
0
+ def schema_migrations_table_name
0
+ Base.table_name_prefix + 'schema_migrations' + Base.table_name_suffix
0
- Base.connection.select_value("SELECT version FROM #{schema_info_table_name}").to_i
0
+ Base.connection.select_values(
0
+ "SELECT version FROM #{schema_migrations_table_name}").map(&:to_i).max || 0
0
def proper_table_name(name)
0
@@ -362,7 +376,7 @@ module ActiveRecord
0
def initialize(direction, migrations_path, target_version = nil)
0
raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations?
0
- Base.connection.initialize_schema_information
0
+ Base.connection.initialize_schema_migrations_table
0
@direction, @migrations_path, @target_version = direction, migrations_path, target_version
0
@@ -383,25 +397,31 @@ module ActiveRecord
0
current = migrations.detect { |m| m.version == current_version }
0
target = migrations.detect { |m| m.version == @target_version }
0
if target.nil? && !@target_version.nil? && @target_version > 0
0
raise UnknownMigrationVersionError.new(@target_version)
0
- start = migrations.index(current) || 0
0
- finish = migrations.index(target) || migrations.size - 1
0
+ start = up? ? 0 : (migrations.index(current) || 0)
0
+ finish = migrations.index(target) || migrations.size - 1
0
runnable = migrations[start..finish]
0
- # skip the current migration if we're heading upwards
0
- runnable.shift if up? && runnable.first == current
0
# skip the last migration if we're headed down, but not ALL the way down
0
runnable.pop if down? && !target.nil?
0
runnable.each do |migration|
0
Base.logger.info "Migrating to #{migration} (#{migration.version})"
0
- migration.migrate(@direction)
0
- set_schema_version_after_migrating(migration)
0
+ # On our way up, we skip migrating the ones we've already migrated
0
+ # On our way down, we skip reverting the ones we've never migrated
0
+ next if up? && migrated.include?(migration.version.to_i)
0
+ if down? && !migrated.include?(migration.version.to_i)
0
+ migration.announce 'never migrated, skipping'; migration.write
0
+ migration.migrate(@direction)
0
+ record_version_state_after_migrating(migration.version)
0
@@ -412,7 +432,7 @@ module ActiveRecord
0
migrations = files.inject([]) do |klasses, file|
0
version, name = file.scan(/([0-9]+)_([_a-z0-9]*).rb/).first
0
- raise IllegalMigrationNameError.new(f) unless version
0
+ raise IllegalMigrationNameError.new(file) unless version
0
if klasses.detect { |m| m.version == version }
0
@@ -433,19 +453,24 @@ module ActiveRecord
0
- migrations.select { |m| m.version > current_version }
0
+ already_migrated = migrated
0
+ migrations.reject { |m| already_migrated.include?(m.version.to_i) }
0
+ sm_table = self.class.schema_migrations_table_name
0
+ Base.connection.select_values("SELECT version FROM #{sm_table}").map(&:to_i).sort
0
- def set_schema_version_after_migrating(migration)
0
- version = migration.version
0
+ def record_version_state_after_migrating(version)
0
+ sm_table = self.class.schema_migrations_table_name
0
- after = migrations[migrations.index(migration) + 1]
0
- version = after ? after.version : 0
0
+ Base.connection.update("DELETE FROM #{sm_table} WHERE version = '#{version}'")
0
+ Base.connection.insert("INSERT INTO #{sm_table} (version) VALUES ('#{version}')")
0
- Base.connection.update("UPDATE #{self.class.schema_info_table_name} SET version = #{version}")
Comments
Removing this method breaks db:structure:dump which breaks db:test:clone_structure; see http://rails.lighthouseapp.com/projects/8994/tickets/21. Not sure how to restore it using the new schema_migrations table.
A fix is contained in a patch which
restores the dump_schema_information method, updated to support the timestamped migrations. See the ticket comments for details: <a
href=”http://rails.lighthouseapp.com/projects/8994/tickets/21#ticket-21-12”>http://rails.lighthouseapp.com/projects/8994/tickets/21#ticket-21-12