aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/migration.rb
diff options
context:
space:
mode:
authorRick Olson <technoweenie@gmail.com>2008-04-09 16:20:15 +0000
committerRick Olson <technoweenie@gmail.com>2008-04-09 16:20:15 +0000
commit8a5a9dcbf64843f064b6e8a0b9c6eea8f0b8536e (patch)
tree27064d2d5ebccdd40bf5c4f43fcf5b2e3e7e57f3 /activerecord/lib/active_record/migration.rb
parent78c2d9fc223e7a9945aee65c838f7ce78e9ddb3e (diff)
downloadrails-8a5a9dcbf64843f064b6e8a0b9c6eea8f0b8536e.tar.gz
rails-8a5a9dcbf64843f064b6e8a0b9c6eea8f0b8536e.tar.bz2
rails-8a5a9dcbf64843f064b6e8a0b9c6eea8f0b8536e.zip
Add support for interleaving migrations by storing which migrations have run in the new schema_migrations table. Closes #11493 [jordi]
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@9244 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
Diffstat (limited to 'activerecord/lib/active_record/migration.rb')
-rw-r--r--activerecord/lib/active_record/migration.rb83
1 files changed, 54 insertions, 29 deletions
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index 4c10cd806c..573c6a4f02 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -123,7 +123,8 @@ module ActiveRecord
#
# To run migrations against the currently configured database, use
# <tt>rake db:migrate</tt>. This will update the database by running all of the
- # pending migrations, creating the <tt>schema_info</tt> table if missing.
+ # pending migrations, creating the <tt>schema_migrations</tt> table
+ # (see "About the schema_migrations table" section below) if missing.
#
# To roll the database back to a previous migration version, use
# <tt>rake db:migrate VERSION=X</tt> where <tt>X</tt> is the version to which
@@ -216,6 +217,21 @@ module ActiveRecord
#
# The phrase "Updating salaries..." would then be printed, along with the
# benchmark for the block when the block completes.
+ #
+ # == About the schema_migrations table
+ #
+ # Rails versions 2.0 and prior used to create a table called
+ # <tt>schema_info</tt> when using migrations. This table contained the
+ # version of the schema as of the last applied migration.
+ #
+ # Starting with Rails 2.1, the <tt>schema_info</tt> table is
+ # (automatically) replaced by the <tt>schema_migrations</tt> table, which
+ # contains the version numbers of all the migrations applied.
+ #
+ # As a result, it is now possible to add migration files that are numbered
+ # lower than the current schema version: when migrating up, those
+ # never-applied "interleaved" migrations will be automatically applied, and
+ # when migrating down, never-applied "interleaved" migrations will be skipped.
class Migration
@@verbose = true
cattr_accessor :verbose
@@ -315,15 +331,12 @@ module ActiveRecord
class << self
def migrate(migrations_path, target_version = nil)
case
- when target_version.nil?, current_version < target_version
- up(migrations_path, target_version)
- when current_version > target_version
- down(migrations_path, target_version)
- when current_version == target_version
- return # You're on the right version
+ 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
-
+
def rollback(migrations_path, steps=1)
migrator = self.new(:down, migrations_path)
start_index = migrator.migrations.index(migrator.current_migration)
@@ -346,12 +359,13 @@ module ActiveRecord
self.new(direction, migrations_path, target_version).run
end
- def schema_info_table_name
- Base.table_name_prefix + "schema_info" + Base.table_name_suffix
+ def schema_migrations_table_name
+ Base.table_name_prefix + 'schema_migrations' + Base.table_name_suffix
end
def current_version
- Base.connection.select_value("SELECT version FROM #{schema_info_table_name}").to_i
+ Base.connection.select_values(
+ "SELECT version FROM #{schema_migrations_table_name}").map(&:to_i).max || 0
end
def proper_table_name(name)
@@ -362,7 +376,7 @@ module ActiveRecord
def initialize(direction, migrations_path, target_version = nil)
raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations?
- Base.connection.initialize_schema_information
+ Base.connection.initialize_schema_migrations_table
@direction, @migrations_path, @target_version = direction, migrations_path, target_version
end
@@ -383,25 +397,31 @@ module ActiveRecord
def migrate
current = migrations.detect { |m| m.version == current_version }
target = migrations.detect { |m| m.version == @target_version }
-
+
if target.nil? && !@target_version.nil? && @target_version > 0
raise UnknownMigrationVersionError.new(@target_version)
end
- start = migrations.index(current) || 0
- finish = migrations.index(target) || migrations.size - 1
+ start = up? ? 0 : (migrations.index(current) || 0)
+ finish = migrations.index(target) || migrations.size - 1
runnable = migrations[start..finish]
- # skip the current migration if we're heading upwards
- runnable.shift if up? && runnable.first == current
-
# skip the last migration if we're headed down, but not ALL the way down
runnable.pop if down? && !target.nil?
runnable.each do |migration|
Base.logger.info "Migrating to #{migration} (#{migration.version})"
- migration.migrate(@direction)
- set_schema_version_after_migrating(migration)
+
+ # On our way up, we skip migrating the ones we've already migrated
+ # On our way down, we skip reverting the ones we've never migrated
+ next if up? && migrated.include?(migration.version.to_i)
+
+ if down? && !migrated.include?(migration.version.to_i)
+ migration.announce 'never migrated, skipping'; migration.write
+ else
+ migration.migrate(@direction)
+ record_version_state_after_migrating(migration.version)
+ end
end
end
@@ -412,7 +432,7 @@ module ActiveRecord
migrations = files.inject([]) do |klasses, file|
version, name = file.scan(/([0-9]+)_([_a-z0-9]*).rb/).first
- raise IllegalMigrationNameError.new(f) unless version
+ raise IllegalMigrationNameError.new(file) unless version
version = version.to_i
if klasses.detect { |m| m.version == version }
@@ -433,19 +453,24 @@ module ActiveRecord
end
def pending_migrations
- migrations.select { |m| m.version > current_version }
+ already_migrated = migrated
+ migrations.reject { |m| already_migrated.include?(m.version.to_i) }
+ end
+
+ def migrated
+ sm_table = self.class.schema_migrations_table_name
+ Base.connection.select_values("SELECT version FROM #{sm_table}").map(&:to_i).sort
end
private
- def set_schema_version_after_migrating(migration)
- version = migration.version
-
+ def record_version_state_after_migrating(version)
+ sm_table = self.class.schema_migrations_table_name
+
if down?
- after = migrations[migrations.index(migration) + 1]
- version = after ? after.version : 0
+ Base.connection.update("DELETE FROM #{sm_table} WHERE version = '#{version}'")
+ else
+ Base.connection.insert("INSERT INTO #{sm_table} (version) VALUES ('#{version}')")
end
-
- Base.connection.update("UPDATE #{self.class.schema_info_table_name} SET version = #{version}")
end
def up?