diff options
Diffstat (limited to 'activerecord/lib/active_record/migration.rb')
-rw-r--r-- | activerecord/lib/active_record/migration.rb | 296 |
1 files changed, 149 insertions, 147 deletions
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 81fe053fe1..ed0c81b639 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -1,5 +1,6 @@ +require "set" +require "zlib" require "active_support/core_ext/module/attribute_accessors" -require 'set' module ActiveRecord class MigrationError < ActiveRecordError#:nodoc: @@ -126,9 +127,9 @@ module ActiveRecord class PendingMigrationError < MigrationError#:nodoc: def initialize(message = nil) if !message && defined?(Rails.env) - super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rails db:migrate RAILS_ENV=#{::Rails.env}") + super("Migrations are pending. To resolve this issue, run:\n\n bin/rails db:migrate RAILS_ENV=#{::Rails.env}") elsif !message - super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rails db:migrate") + super("Migrations are pending. To resolve this issue, run:\n\n bin/rails db:migrate") else super end @@ -145,7 +146,7 @@ module ActiveRecord class NoEnvironmentInSchemaError < MigrationError #:nodoc: def initialize - msg = "Environment data not found in the schema. To resolve this issue, run: \n\n\tbin/rails db:environment:set" + msg = "Environment data not found in the schema. To resolve this issue, run: \n\n bin/rails db:environment:set" if defined?(Rails.env) super("#{msg} RAILS_ENV=#{::Rails.env}") else @@ -168,7 +169,7 @@ module ActiveRecord msg = "You are attempting to modify a database that was last run in `#{ stored }` environment.\n" msg << "You are running in `#{ current }` environment. " msg << "If you are sure you want to continue, first set the environment using:\n\n" - msg << "\tbin/rails db:environment:set" + msg << " bin/rails db:environment:set" if defined?(Rails.env) super("#{msg} RAILS_ENV=#{::Rails.env}\n\n") else @@ -276,8 +277,10 @@ module ActiveRecord # # * <tt>change_column(table_name, column_name, type, options)</tt>: Changes # the column to a different type using the same parameters as add_column. - # * <tt>change_column_default(table_name, column_name, default)</tt>: Sets a - # default value for +column_name+ defined by +default+ on +table_name+. + # * <tt>change_column_default(table_name, column_name, default_or_changes)</tt>: + # Sets a default value for +column_name+ defined by +default_or_changes+ on + # +table_name+. Passing a hash containing <tt>:from</tt> and <tt>:to</tt> + # as +default_or_changes+ will make this change reversible in the migration. # * <tt>change_column_null(table_name, column_name, null, default = nil)</tt>: # Sets or removes a +NOT NULL+ constraint on +column_name+. The +null+ flag # indicates whether the value can be +NULL+. See @@ -509,8 +512,8 @@ module ActiveRecord # Remember that you can still open your own transactions, even if you # are in a Migration with <tt>self.disable_ddl_transaction!</tt>. class Migration - autoload :CommandRecorder, 'active_record/migration/command_recorder' - autoload :Compatibility, 'active_record/migration/compatibility' + autoload :CommandRecorder, "active_record/migration/command_recorder" + autoload :Compatibility, "active_record/migration/compatibility" # This must be defined before the inherited hook, below class Current < Migration # :nodoc: @@ -519,7 +522,10 @@ module ActiveRecord def self.inherited(subclass) # :nodoc: super if subclass.superclass == Migration - subclass.include Compatibility::Legacy + raise StandardError, "Directly inheriting from ActiveRecord::Migration is not supported. " \ + "Please specify the Rails release the migration was written for:\n" \ + "\n" \ + " class #{self.class.name} < ActiveRecord::Migration[4.2]" end end @@ -554,9 +560,9 @@ module ActiveRecord private - def connection - ActiveRecord::Base.connection - end + def connection + ActiveRecord::Base.connection + end end class << self @@ -724,7 +730,7 @@ module ActiveRecord # end def reversible helper = ReversibleBlockHelper.new(reverting?) - execute_block{ yield helper } + execute_block { yield helper } end # Runs the given migration classes. @@ -766,7 +772,7 @@ module ActiveRecord when :down then announce "reverting" end - time = nil + time = nil ActiveRecord::Base.connection_pool.with_connection do |conn| time = Benchmark.measure do exec_migration(conn, direction) @@ -794,7 +800,7 @@ module ActiveRecord @connection = nil end - def write(text="") + def write(text = "") puts(text) if verbose end @@ -804,7 +810,7 @@ module ActiveRecord write "== %s %s" % [text, "=" * length] end - def say(message, subitem=false) + def say(message, subitem = false) write "#{subitem ? " ->" : "--"} #{message}" end @@ -829,7 +835,7 @@ module ActiveRecord end def method_missing(method, *arguments, &block) - arg_list = arguments.map(&:inspect) * ', ' + arg_list = arguments.map(&:inspect) * ", " say_with_time "#{method}(#{arg_list})" do unless connection.respond_to? :revert @@ -921,19 +927,18 @@ module ActiveRecord end private - def execute_block - if connection.respond_to? :execute_block - super # use normal delegation to record the block - else - yield + def execute_block + if connection.respond_to? :execute_block + super # use normal delegation to record the block + else + yield + end end - end end # MigrationProxy is used to defer loading of the actual migration classes # until they are needed class MigrationProxy < Struct.new(:name, :version, :filename, :scope) - def initialize(name, version, filename, scope) super @migration = nil @@ -959,7 +964,6 @@ module ActiveRecord require(File.expand_path(filename)) name.constantize.new(name, version) end - end class NullMigration < MigrationProxy #:nodoc: @@ -990,11 +994,11 @@ module ActiveRecord end end - def rollback(migrations_paths, steps=1) + def rollback(migrations_paths, steps = 1) move(:down, migrations_paths, steps) end - def forward(migrations_paths, steps=1) + def forward(migrations_paths, steps = 1) move(:up, migrations_paths, steps) end @@ -1025,12 +1029,10 @@ module ActiveRecord end def get_all_versions(connection = Base.connection) - ActiveSupport::Deprecation.silence do - if connection.table_exists?(schema_migrations_table_name) - SchemaMigration.all.map { |x| x.version.to_i }.sort - else - [] - end + if connection.table_exists?(schema_migrations_table_name) + SchemaMigration.all.map { |x| x.version.to_i }.sort + else + [] end end @@ -1051,13 +1053,13 @@ module ActiveRecord end def migrations_paths - @migrations_paths ||= ['db/migrate'] + @migrations_paths ||= ["db/migrate"] # just to not break things if someone uses: migrations_path = some_string Array(@migrations_paths) end def match_to_migration_filename?(filename) # :nodoc: - File.basename(filename) =~ Migration::MigrationFilenameRegexp + Migration::MigrationFilenameRegexp.match?(File.basename(filename)) end def parse_migration_filename(filename) # :nodoc: @@ -1164,146 +1166,146 @@ module ActiveRecord private - # Used for running a specific migration. - def run_without_lock - migration = migrations.detect { |m| m.version == @target_version } - raise UnknownMigrationVersionError.new(@target_version) if migration.nil? - execute_migration_in_transaction(migration, @direction) - - record_environment - end + # Used for running a specific migration. + def run_without_lock + migration = migrations.detect { |m| m.version == @target_version } + raise UnknownMigrationVersionError.new(@target_version) if migration.nil? + execute_migration_in_transaction(migration, @direction) - # Used for running multiple migrations up to or down to a certain value. - def migrate_without_lock - if invalid_target? - raise UnknownMigrationVersionError.new(@target_version) + record_environment end - runnable.each do |migration| - execute_migration_in_transaction(migration, @direction) - end + # Used for running multiple migrations up to or down to a certain value. + def migrate_without_lock + if invalid_target? + raise UnknownMigrationVersionError.new(@target_version) + end - record_environment - end + runnable.each do |migration| + execute_migration_in_transaction(migration, @direction) + end - # Stores the current environment in the database. - def record_environment - return if down? - ActiveRecord::InternalMetadata[:environment] = ActiveRecord::Migrator.current_environment - end + record_environment + end - def ran?(migration) - migrated.include?(migration.version.to_i) - end + # Stores the current environment in the database. + def record_environment + return if down? + ActiveRecord::InternalMetadata[:environment] = ActiveRecord::Migrator.current_environment + end - # Return true if a valid version is not provided. - def invalid_target? - !target && @target_version && @target_version > 0 - end + def ran?(migration) + migrated.include?(migration.version.to_i) + end - def execute_migration_in_transaction(migration, direction) - return if down? && !migrated.include?(migration.version.to_i) - return if up? && migrated.include?(migration.version.to_i) + # Return true if a valid version is not provided. + def invalid_target? + !target && @target_version && @target_version > 0 + end + + def execute_migration_in_transaction(migration, direction) + return if down? && !migrated.include?(migration.version.to_i) + return if up? && migrated.include?(migration.version.to_i) - Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger + Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger - ddl_transaction(migration) do - migration.migrate(direction) - record_version_state_after_migrating(migration.version) + ddl_transaction(migration) do + migration.migrate(direction) + record_version_state_after_migrating(migration.version) + end + rescue => e + msg = "An error has occurred, " + msg << "this and " if use_transaction?(migration) + msg << "all later migrations canceled:\n\n#{e}" + raise StandardError, msg, e.backtrace end - rescue => e - msg = "An error has occurred, " - msg << "this and " if use_transaction?(migration) - msg << "all later migrations canceled:\n\n#{e}" - raise StandardError, msg, e.backtrace - end - def target - migrations.detect { |m| m.version == @target_version } - end + def target + migrations.detect { |m| m.version == @target_version } + end - def finish - migrations.index(target) || migrations.size - 1 - end + def finish + migrations.index(target) || migrations.size - 1 + end - def start - up? ? 0 : (migrations.index(current) || 0) - end + def start + up? ? 0 : (migrations.index(current) || 0) + end - def validate(migrations) - name ,= migrations.group_by(&:name).find { |_,v| v.length > 1 } - raise DuplicateMigrationNameError.new(name) if name + def validate(migrations) + name , = migrations.group_by(&:name).find { |_, v| v.length > 1 } + raise DuplicateMigrationNameError.new(name) if name - version ,= migrations.group_by(&:version).find { |_,v| v.length > 1 } - raise DuplicateMigrationVersionError.new(version) if version - end + version , = migrations.group_by(&:version).find { |_, v| v.length > 1 } + raise DuplicateMigrationVersionError.new(version) if version + end - def record_version_state_after_migrating(version) - if down? - migrated.delete(version) - ActiveRecord::SchemaMigration.where(:version => version.to_s).delete_all - else - migrated << version - ActiveRecord::SchemaMigration.create!(version: version.to_s) + def record_version_state_after_migrating(version) + if down? + migrated.delete(version) + ActiveRecord::SchemaMigration.where(version: version.to_s).delete_all + else + migrated << version + ActiveRecord::SchemaMigration.create!(version: version.to_s) + end end - end - def self.last_stored_environment - return nil if current_version == 0 - raise NoEnvironmentInSchemaError unless ActiveRecord::InternalMetadata.table_exists? + def self.last_stored_environment + return nil if current_version == 0 + raise NoEnvironmentInSchemaError unless ActiveRecord::InternalMetadata.table_exists? - environment = ActiveRecord::InternalMetadata[:environment] - raise NoEnvironmentInSchemaError unless environment - environment - end + environment = ActiveRecord::InternalMetadata[:environment] + raise NoEnvironmentInSchemaError unless environment + environment + end - def self.current_environment - ActiveRecord::ConnectionHandling::DEFAULT_ENV.call - end + def self.current_environment + ActiveRecord::ConnectionHandling::DEFAULT_ENV.call + end - def self.protected_environment? - ActiveRecord::Base.protected_environments.include?(last_stored_environment) if last_stored_environment - end + def self.protected_environment? + ActiveRecord::Base.protected_environments.include?(last_stored_environment) if last_stored_environment + end - def up? - @direction == :up - end + def up? + @direction == :up + end - def down? - @direction == :down - end + def down? + @direction == :down + end - # Wrap the migration in a transaction only if supported by the adapter. - def ddl_transaction(migration) - if use_transaction?(migration) - Base.transaction { yield } - else - yield + # Wrap the migration in a transaction only if supported by the adapter. + def ddl_transaction(migration) + if use_transaction?(migration) + Base.transaction { yield } + else + yield + end end - end - def use_transaction?(migration) - !migration.disable_ddl_transaction && Base.connection.supports_ddl_transactions? - end + def use_transaction?(migration) + !migration.disable_ddl_transaction && Base.connection.supports_ddl_transactions? + end - def use_advisory_lock? - Base.connection.supports_advisory_locks? - end + def use_advisory_lock? + Base.connection.supports_advisory_locks? + end - def with_advisory_lock - lock_id = generate_migrator_advisory_lock_id - got_lock = Base.connection.get_advisory_lock(lock_id) - raise ConcurrentMigrationError unless got_lock - load_migrated # reload schema_migrations to be sure it wasn't changed by another process before we got the lock - yield - ensure - Base.connection.release_advisory_lock(lock_id) if got_lock - end + def with_advisory_lock + lock_id = generate_migrator_advisory_lock_id + got_lock = Base.connection.get_advisory_lock(lock_id) + raise ConcurrentMigrationError unless got_lock + load_migrated # reload schema_migrations to be sure it wasn't changed by another process before we got the lock + yield + ensure + Base.connection.release_advisory_lock(lock_id) if got_lock + end - MIGRATOR_SALT = 2053462845 - def generate_migrator_advisory_lock_id - db_name_hash = Zlib.crc32(Base.connection.current_database) - MIGRATOR_SALT * db_name_hash - end + MIGRATOR_SALT = 2053462845 + def generate_migrator_advisory_lock_id + db_name_hash = Zlib.crc32(Base.connection.current_database) + MIGRATOR_SALT * db_name_hash + end end end |