2 class IrreversibleMigration
< ActiveRecordError
#:nodoc:
5 class DuplicateMigrationVersionError
< ActiveRecordError
#:nodoc:
6 def initialize(version)
7 super("Multiple migrations have the version number #{version}")
11 class DuplicateMigrationNameError
< ActiveRecordError
#:nodoc:
13 super("Multiple migrations have the name #{name}")
17 class UnknownMigrationVersionError
< ActiveRecordError
#:nodoc:
18 def initialize(version)
19 super("No migration with version number #{version}")
23 class IllegalMigrationNameError
< ActiveRecordError
#:nodoc:
25 super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed)")
29 # Migrations can manage the evolution of a schema used by several physical databases. It's a solution
30 # to the common problem of adding a field to make a new feature work in your local database, but being unsure of how to
31 # push that change to other developers and to the production server. With migrations, you can describe the transformations
32 # in self-contained classes that can be checked into version control systems and executed against another database that
33 # might be one, two, or five versions behind.
35 # Example of a simple migration:
37 # class AddSsl < ActiveRecord::Migration
39 # add_column :accounts, :ssl_enabled, :boolean, :default => 1
43 # remove_column :accounts, :ssl_enabled
47 # This migration will add a boolean flag to the accounts table and remove it if you're backing out of the migration.
48 # It shows how all migrations have two class methods +up+ and +down+ that describes the transformations required to implement
49 # or remove the migration. These methods can consist of both the migration specific methods like add_column and remove_column,
50 # but may also contain regular Ruby code for generating data needed for the transformations.
52 # Example of a more complex migration that also needs to initialize data:
54 # class AddSystemSettings < ActiveRecord::Migration
56 # create_table :system_settings do |t|
64 # SystemSetting.create :name => "notice", :label => "Use notice?", :value => 1
68 # drop_table :system_settings
72 # This migration first adds the system_settings table, then creates the very first row in it using the Active Record model
73 # that relies on the table. It also uses the more advanced create_table syntax where you can specify a complete table schema
76 # == Available transformations
78 # * <tt>create_table(name, options)</tt> Creates a table called +name+ and makes the table object available to a block
79 # that can then add columns to it, following the same format as add_column. See example above. The options hash is for
80 # fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create table definition.
81 # * <tt>drop_table(name)</tt>: Drops the table called +name+.
82 # * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+ to +new_name+.
83 # * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column to the table called +table_name+
84 # named +column_name+ specified to be one of the following types:
85 # <tt>:string</tt>, <tt>:text</tt>, <tt>:integer</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
86 # <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>. A default value can be specified by passing an
87 # +options+ hash like <tt>{ :default => 11 }</tt>. Other options include <tt>:limit</tt> and <tt>:null</tt> (e.g. <tt>{ :limit => 50, :null => false }</tt>)
88 # -- see ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
89 # * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames a column but keeps the type and content.
90 # * <tt>change_column(table_name, column_name, type, options)</tt>: Changes the column to a different type using the same
91 # parameters as add_column.
92 # * <tt>remove_column(table_name, column_name)</tt>: Removes the column named +column_name+ from the table called +table_name+.
93 # * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index with the name of the column. Other options include
94 # <tt>:name</tt> and <tt>:unique</tt> (e.g. <tt>{ :name => "users_name_index", :unique => true }</tt>).
95 # * <tt>remove_index(table_name, index_name)</tt>: Removes the index specified by +index_name+.
97 # == Irreversible transformations
99 # Some transformations are destructive in a manner that cannot be reversed. Migrations of that kind should raise
100 # an <tt>ActiveRecord::IrreversibleMigration</tt> exception in their +down+ method.
102 # == Running migrations from within Rails
104 # The Rails package has several tools to help create and apply migrations.
106 # To generate a new migration, you can use
107 # script/generate migration MyNewMigration
109 # where MyNewMigration is the name of your migration. The generator will
110 # create an empty migration file <tt>nnn_my_new_migration.rb</tt> in the <tt>db/migrate/</tt>
111 # directory where <tt>nnn</tt> is the next largest migration number.
113 # You may then edit the <tt>self.up</tt> and <tt>self.down</tt> methods of
116 # There is a special syntactic shortcut to generate migrations that add fields to a table.
117 # script/generate migration add_fieldname_to_tablename fieldname:string
119 # This will generate the file <tt>nnn_add_fieldname_to_tablename</tt>, which will look like this:
120 # class AddFieldnameToTablename < ActiveRecord::Migration
122 # add_column :tablenames, :fieldname, :string
126 # remove_column :tablenames, :fieldname
130 # To run migrations against the currently configured database, use
131 # <tt>rake db:migrate</tt>. This will update the database by running all of the
132 # pending migrations, creating the <tt>schema_migrations</tt> table
133 # (see "About the schema_migrations table" section below) if missing.
135 # To roll the database back to a previous migration version, use
136 # <tt>rake db:migrate VERSION=X</tt> where <tt>X</tt> is the version to which
137 # you wish to downgrade. If any of the migrations throw an
138 # <tt>ActiveRecord::IrreversibleMigration</tt> exception, that step will fail and you'll
139 # have some manual work to do.
141 # == Database support
143 # Migrations are currently supported in MySQL, PostgreSQL, SQLite,
144 # SQL Server, Sybase, and Oracle (all supported databases except DB2).
148 # Not all migrations change the schema. Some just fix the data:
150 # class RemoveEmptyTags < ActiveRecord::Migration
152 # Tag.find(:all).each { |tag| tag.destroy if tag.pages.empty? }
156 # # not much we can do to restore deleted data
157 # raise ActiveRecord::IrreversibleMigration, "Can't recover the deleted tags"
161 # Others remove columns when they migrate up instead of down:
163 # class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration
165 # remove_column :items, :incomplete_items_count
166 # remove_column :items, :completed_items_count
170 # add_column :items, :incomplete_items_count
171 # add_column :items, :completed_items_count
175 # And sometimes you need to do something in SQL not abstracted directly by migrations:
177 # class MakeJoinUnique < ActiveRecord::Migration
179 # execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
183 # execute "ALTER TABLE `pages_linked_pages` DROP INDEX `page_id_linked_page_id`"
187 # == Using a model after changing its table
189 # Sometimes you'll want to add a column in a migration and populate it immediately after. In that case, you'll need
190 # to make a call to Base#reset_column_information in order to ensure that the model has the latest column data from
191 # after the new column was added. Example:
193 # class AddPeopleSalary < ActiveRecord::Migration
195 # add_column :people, :salary, :integer
196 # Person.reset_column_information
197 # Person.find(:all).each do |p|
198 # p.update_attribute :salary, SalaryCalculator.compute(p)
203 # == Controlling verbosity
205 # By default, migrations will describe the actions they are taking, writing
206 # them to the console as they happen, along with benchmarks describing how
207 # long each step took.
209 # You can quiet them down by setting ActiveRecord::Migration.verbose = false.
211 # You can also insert your own messages and benchmarks by using the +say_with_time+
216 # say_with_time "Updating salaries..." do
217 # Person.find(:all).each do |p|
218 # p.update_attribute :salary, SalaryCalculator.compute(p)
224 # The phrase "Updating salaries..." would then be printed, along with the
225 # benchmark for the block when the block completes.
227 # == About the schema_migrations table
229 # Rails versions 2.0 and prior used to create a table called
230 # <tt>schema_info</tt> when using migrations. This table contained the
231 # version of the schema as of the last applied migration.
233 # Starting with Rails 2.1, the <tt>schema_info</tt> table is
234 # (automatically) replaced by the <tt>schema_migrations</tt> table, which
235 # contains the version numbers of all the migrations applied.
237 # As a result, it is now possible to add migration files that are numbered
238 # lower than the current schema version: when migrating up, those
239 # never-applied "interleaved" migrations will be automatically applied, and
240 # when migrating down, never-applied "interleaved" migrations will be skipped.
242 # == Timestamped Migrations
244 # By default, Rails generates migrations that look like:
246 # 20080717013526_your_migration_name.rb
248 # The prefix is a generation timestamp (in UTC).
250 # If you'd prefer to use numeric prefixes, you can turn timestamped migrations
253 # config.active_record.timestamped_migrations = false
259 cattr_accessor
:verbose
262 def up_with_benchmarks
#:nodoc:
266 def down_with_benchmarks
#:nodoc:
270 # Execute this migration in the named direction
271 def migrate(direction
)
272 return unless respond_to
?(direction
)
275 when :up then announce
"migrating"
276 when :down then announce
"reverting"
280 time
= Benchmark
.measure
{ result
= send("#{direction}_without_benchmarks") }
283 when :up then announce
"migrated (%.4fs)" % time
.real
; write
284 when :down then announce
"reverted (%.4fs)" % time
.real
; write
290 # Because the method added may do an alias_method, it can be invoked
291 # recursively. We use @ignore_new_methods as a guard to indicate whether
292 # it is safe for the call to proceed.
293 def singleton_method_added(sym
) #:nodoc:
294 return if defined?(@ignore_new_methods) && @ignore_new_methods
297 @ignore_new_methods = true
301 klass
= (class << self; self; end)
302 klass
.send(:alias_method_chain, sym
, "benchmarks")
305 @ignore_new_methods = false
310 puts(text
) if verbose
313 def announce(message
)
314 text
= "#{@version} #{name}: #{message}"
315 length
= [0, 75 - text
.length
].max
316 write
"== %s %s" % [text
, "=" * length
]
319 def say(message
, subitem
=false)
320 write
"#{subitem ? " ->" : "--"} #{message}"
323 def say_with_time(message
)
326 time
= Benchmark
.measure
{ result
= yield }
327 say
"%.4fs" % time
.real
, :subitem
328 say("#{result} rows", :subitem) if result
.is_a
?(Integer
)
332 def suppress_messages
333 save
, self.verbose
= verbose
, false
339 def method_missing(method
, *arguments
, &block
)
340 arg_list
= arguments
.map(&:inspect) * ', '
342 say_with_time
"#{method}(#{arg_list})" do
343 unless arguments
.empty
? || method
== :execute
344 arguments
[0] = Migrator
.proper_table_name(arguments
.first
)
346 ActiveRecord
::Base.connection
.send(method
, *arguments
, &block
)
352 # MigrationProxy is used to defer loading of the actual migration classes
353 # until they are needed
356 attr_accessor
:name, :version, :filename
358 delegate
:migrate, :announce, :write, :to=>:migration
363 @migration ||= load_migration
373 class Migrator
#:nodoc:
375 def migrate(migrations_path
, target_version
= nil)
377 when target_version
.nil? then up(migrations_path
, target_version
)
378 when current_version
> target_version
then down(migrations_path
, target_version
)
379 else up(migrations_path
, target_version
)
383 def rollback(migrations_path
, steps
=1)
384 migrator
= self.new(:down, migrations_path
)
385 start_index
= migrator
.migrations
.index(migrator
.current_migration
)
387 return unless start_index
389 finish
= migrator
.migrations
[start_index
+ steps
]
390 down(migrations_path
, finish
? finish
.version : 0)
393 def up(migrations_path
, target_version
= nil)
394 self.new(:up, migrations_path
, target_version
).migrate
397 def down(migrations_path
, target_version
= nil)
398 self.new(:down, migrations_path
, target_version
).migrate
401 def run(direction
, migrations_path
, target_version
)
402 self.new(direction
, migrations_path
, target_version
).run
405 def schema_migrations_table_name
406 Base
.table_name_prefix
+ 'schema_migrations' + Base
.table_name_suffix
410 Base
.connection
.select_values("SELECT version FROM #{schema_migrations_table_name}").map(&:to_i).sort
414 sm_table
= schema_migrations_table_name
415 if Base
.connection
.table_exists
?(sm_table
)
416 get_all_versions
.max
|| 0
422 def proper_table_name(name
)
423 # Use the Active Record objects own table_name, or pre/suffix from ActiveRecord::Base if name is a symbol/string
424 name
.table_name
rescue "#{ActiveRecord::Base.table_name_prefix}#{name}#{ActiveRecord::Base.table_name_suffix}"
428 def initialize(direction
, migrations_path
, target_version
= nil)
429 raise StandardError
.new("This database does not yet support migrations") unless Base
.connection
.supports_migrations
?
430 Base
.connection
.initialize_schema_migrations_table
431 @direction, @migrations_path, @target_version = direction
, migrations_path
, target_version
438 def current_migration
439 migrations
.detect
{ |m
| m
.version == current_version
}
443 target
= migrations
.detect
{ |m
| m
.version == @target_version }
444 raise UnknownMigrationVersionError
.new(@target_version) if target
.nil?
445 unless (up
? && migrated
.include?(target
.version.to_i
)) || (down
? && !migrated
.include?(target
.version.to_i
))
446 target
.migrate(@direction)
447 record_version_state_after_migrating(target
.version)
452 current
= migrations
.detect
{ |m
| m
.version == current_version
}
453 target
= migrations
.detect
{ |m
| m
.version == @target_version }
455 if target
.nil? && !@target_version.nil? && @target_version > 0
456 raise UnknownMigrationVersionError
.new(@target_version)
459 start
= up
? ? 0 : (migrations
.index(current
) || 0)
460 finish
= migrations
.index(target
) || migrations
.size
- 1
461 runnable
= migrations
[start
..finish
]
463 # skip the last migration if we're headed down, but not ALL the way down
464 runnable
.pop
if down
? && !target
.nil?
466 runnable
.each
do |migration
|
467 Base
.logger
.info
"Migrating to #{migration.name} (#{migration.version})"
469 # On our way up, we skip migrating the ones we've already migrated
470 next if up
? && migrated
.include?(migration
.version.to_i
)
472 # On our way down, we skip reverting the ones we've never migrated
473 if down
? && !migrated
.include?(migration
.version.to_i
)
474 migration
.announce
'never migrated, skipping'; migration
.write
480 migration
.migrate(@direction)
481 record_version_state_after_migrating(migration
.version)
484 canceled_msg
= Base
.connection
.supports_ddl_transactions
? ? "this and " : ""
485 raise StandardError
, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e
.backtrace
491 @migrations ||= begin
492 files
= Dir
["#{@migrations_path}/[0-9]*_*.rb"]
494 migrations
= files
.inject([]) do |klasses
, file
|
495 version, name
= file
.scan(/([0-9]+)_([_a-z0-9]*).rb/).first
497 raise IllegalMigrationNameError
.new(file
) unless version
498 version = version.to_i
500 if klasses
.detect
{ |m
| m
.version == version }
501 raise DuplicateMigrationVersionError
.new(version)
504 if klasses
.detect
{ |m
| m
.name
== name
.camelize
}
505 raise DuplicateMigrationNameError
.new(name
.camelize
)
508 klasses
<< returning(MigrationProxy
.new
) do |migration
|
509 migration
.name
= name
.camelize
510 migration
.version = version
511 migration
.filename
= file
515 migrations
= migrations
.sort_by(&:version)
516 down
? ? migrations
.reverse
: migrations
520 def pending_migrations
521 already_migrated
= migrated
522 migrations
.reject
{ |m
| already_migrated
.include?(m
.version.to_i
) }
526 @migrated_versions ||= self.class.get_all_versions
530 def record_version_state_after_migrating(version)
531 sm_table
= self.class.schema_migrations_table_name
533 @migrated_versions ||= []
535 @migrated_versions.delete(version.to_i
)
536 Base
.connection
.update("DELETE FROM #{sm_table} WHERE version = '#{version}'")
538 @migrated_versions.push(version.to_i
).sort
!
539 Base
.connection
.insert("INSERT INTO #{sm_table} (version) VALUES ('#{version}')")
551 # Wrap the migration in a transaction only if supported by the adapter.
552 def ddl_transaction(&block
)
553 if Base
.connection
.supports_ddl_transactions
?
554 Base
.transaction
{ block
.call
}