migration_test.rb 38.9 KB
Newer Older
1 2
require 'cases/helper'
require 'cases/migration/helper'
3
require 'bigdecimal/util'
4
require 'concurrent/atomic/count_down_latch'
5

J
Jeremy Kemper 已提交
6 7
require 'models/person'
require 'models/topic'
8
require 'models/developer'
A
Arun Agrawal 已提交
9
require 'models/computer'
10 11

require MIGRATIONS_ROOT + "/valid/2_we_need_reminders"
12 13
require MIGRATIONS_ROOT + "/rename/1_we_need_things"
require MIGRATIONS_ROOT + "/rename/2_rename_things"
14
require MIGRATIONS_ROOT + "/decimal/1_give_me_big_numbers"
15

16
class BigNumber < ActiveRecord::Base
17
  unless current_adapter?(:PostgreSQLAdapter, :SQLite3Adapter)
18
    attribute :value_of_e, :integer
19
  end
20
  attribute :my_house_population, :integer
21
end
22

23
class Reminder < ActiveRecord::Base; end
24

25
class Thing < ActiveRecord::Base; end
26

27
class MigrationTest < ActiveRecord::TestCase
28
  self.use_transactional_tests = false
29

30
  fixtures :people
31

32 33
  def setup
    super
A
Akira Matsuda 已提交
34
    %w(reminders people_reminders prefix_reminders_suffix p_things_s).each do |table|
A
Aaron Patterson 已提交
35 36 37
      Reminder.connection.drop_table(table) rescue nil
    end
    Reminder.reset_column_information
38
    @verbose_was, ActiveRecord::Migration.verbose = ActiveRecord::Migration.verbose, false
A
Akira Matsuda 已提交
39
    ActiveRecord::Base.connection.schema_cache.clear!
40
  end
41

G
Guo Xiang Tan 已提交
42
  teardown do
43 44 45
    ActiveRecord::Base.table_name_prefix = ""
    ActiveRecord::Base.table_name_suffix = ""

46 47
    ActiveRecord::Base.connection.initialize_schema_migrations_table
    ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::Migrator.schema_migrations_table_name}"
48

49
    %w(things awesome_things prefix_things_suffix p_awesome_things_s ).each do |table|
50
      Thing.connection.drop_table(table) rescue nil
51
    end
52
    Thing.reset_column_information
53

54 55 56
    %w(reminders people_reminders prefix_reminders_suffix).each do |table|
      Reminder.connection.drop_table(table) rescue nil
    end
57
    Reminder.reset_table_name
58
    Reminder.reset_column_information
59

60 61 62 63 64 65
    %w(last_name key bio age height wealth birthday favorite_day
       moment_of_truth male administrator funny).each do |column|
      Person.connection.remove_column('people', column) rescue nil
    end
    Person.connection.remove_column("people", "first_name") rescue nil
    Person.connection.remove_column("people", "middle_name") rescue nil
66
    Person.connection.add_column("people", "first_name", :string)
67
    Person.reset_column_information
68 69

    ActiveRecord::Migration.verbose = @verbose_was
70
  end
71

72 73
  def test_migrator_versions
    migrations_path = MIGRATIONS_ROOT + "/valid"
74
    old_path = ActiveRecord::Migrator.migrations_paths
75 76 77 78
    ActiveRecord::Migrator.migrations_paths = migrations_path

    ActiveRecord::Migrator.up(migrations_path)
    assert_equal 3, ActiveRecord::Migrator.current_version
79
    assert_equal false, ActiveRecord::Migrator.needs_migration?
80 81 82

    ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid")
    assert_equal 0, ActiveRecord::Migrator.current_version
83
    assert_equal true, ActiveRecord::Migrator.needs_migration?
84

Y
Yves Senn 已提交
85
    ActiveRecord::SchemaMigration.create!(version: 3)
86 87 88 89 90 91
    assert_equal true, ActiveRecord::Migrator.needs_migration?
  ensure
    ActiveRecord::Migrator.migrations_paths = old_path
  end

  def test_migration_detection_without_schema_migration_table
92
    ActiveRecord::Base.connection.drop_table 'schema_migrations', if_exists: true
93 94 95 96 97 98

    migrations_path = MIGRATIONS_ROOT + "/valid"
    old_path = ActiveRecord::Migrator.migrations_paths
    ActiveRecord::Migrator.migrations_paths = migrations_path

    assert_equal true, ActiveRecord::Migrator.needs_migration?
99 100
  ensure
    ActiveRecord::Migrator.migrations_paths = old_path
101 102
  end

103 104 105 106 107 108 109 110 111 112 113 114 115
  def test_any_migrations
    old_path = ActiveRecord::Migrator.migrations_paths
    ActiveRecord::Migrator.migrations_paths = MIGRATIONS_ROOT + "/valid"

    assert ActiveRecord::Migrator.any_migrations?

    ActiveRecord::Migrator.migrations_paths = MIGRATIONS_ROOT + "/empty"

    assert_not ActiveRecord::Migrator.any_migrations?
  ensure
    ActiveRecord::Migrator.migrations_paths = old_path
  end

116
  def test_migration_version
117
    assert_nothing_raised { ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/version_check", 20131219224947) }
118 119
  end

120 121 122 123
  def test_create_table_with_force_true_does_not_drop_nonexisting_table
    # using a copy as we need the drop_table method to
    # continue to work for the ensure block of the test
    temp_conn = Person.connection.dup
124 125 126

    assert_not_equal temp_conn, Person.connection

127 128
    temp_conn.create_table :testings2, :force => true do |t|
      t.column :foo, :string
129
    end
130
  ensure
131
    Person.connection.drop_table :testings2, if_exists: true
132
  end
133

134
  def test_migration_instance_has_connection
135
    migration = Class.new(ActiveRecord::Migration::Current).new
136
    assert_equal ActiveRecord::Base.connection, migration.connection
137
  end
138

139
  def test_method_missing_delegates_to_connection
140
    migration = Class.new(ActiveRecord::Migration::Current) {
141 142 143 144 145 146
      def connection
        Class.new {
          def create_table; "hi mom!"; end
        }.new
      end
    }.new
147

148
    assert_equal "hi mom!", migration.method_missing(:create_table)
149
  end
150

151 152 153 154 155
  def test_add_table_with_decimals
    Person.connection.drop_table :big_numbers rescue nil

    assert !BigNumber.table_exists?
    GiveMeBigNumbers.up
156
    BigNumber.reset_column_information
157 158 159 160 161 162 163 164 165

    assert BigNumber.create(
      :bank_balance => 1586.43,
      :big_bank_balance => BigDecimal("1000234000567.95"),
      :world_population => 6000000000,
      :my_house_population => 3,
      :value_of_e => BigDecimal("2.7182818284590452353602875")
    )

166
    b = BigNumber.first
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
    assert_not_nil b

    assert_not_nil b.bank_balance
    assert_not_nil b.big_bank_balance
    assert_not_nil b.world_population
    assert_not_nil b.my_house_population
    assert_not_nil b.value_of_e

    # TODO: set world_population >= 2**62 to cover 64-bit platforms and test
    # is_a?(Bignum)
    assert_kind_of Integer, b.world_population
    assert_equal 6000000000, b.world_population
    assert_kind_of Fixnum, b.my_house_population
    assert_equal 3, b.my_house_population
    assert_kind_of BigDecimal, b.bank_balance
    assert_equal BigDecimal("1586.43"), b.bank_balance
    assert_kind_of BigDecimal, b.big_bank_balance
    assert_equal BigDecimal("1000234000567.95"), b.big_bank_balance

    # This one is fun. The 'value_of_e' field is defined as 'DECIMAL' with
    # precision/scale explicitly left out.  By the SQL standard, numbers
    # assigned to this field should be truncated but that's seldom respected.
    if current_adapter?(:PostgreSQLAdapter)
      # - PostgreSQL changes the SQL spec on columns declared simply as
      # "decimal" to something more useful: instead of being given a scale
      # of 0, they take on the compile-time limit for precision and scale,
      # so the following should succeed unless you have used really wacky
      # compilation options
      assert_kind_of BigDecimal, b.value_of_e
      assert_equal BigDecimal("2.7182818284590452353602875"), b.value_of_e
    elsif current_adapter?(:SQLite3Adapter)
      # - SQLite3 stores a float, in violation of SQL
      assert_kind_of BigDecimal, b.value_of_e
      assert_in_delta BigDecimal("2.71828182845905"), b.value_of_e, 0.00000000000001
    else
      # - SQL standard is an integer
      assert_kind_of Fixnum, b.value_of_e
      assert_equal 2, b.value_of_e
    end

    GiveMeBigNumbers.down
208
    assert_raise(ActiveRecord::StatementInvalid) { BigNumber.first }
209
  end
210

211
  def test_filtering_migrations
212
    assert_no_column Person, :last_name
213
    assert !Reminder.table_exists?
214

215 216
    name_filter = lambda { |migration| migration.name == "ValidPeopleHaveLastNames" }
    ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", &name_filter)
217

218
    assert_column Person, :last_name
219
    assert_raise(ActiveRecord::StatementInvalid) { Reminder.first }
220

221 222
    ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", &name_filter)

223
    assert_no_column Person, :last_name
224
    assert_raise(ActiveRecord::StatementInvalid) { Reminder.first }
225 226
  end

227
  class MockMigration < ActiveRecord::Migration::Current
228 229 230 231
    attr_reader :went_up, :went_down
    def initialize
      @went_up   = false
      @went_down = false
232 233
    end

234 235 236 237
    def up
      @went_up = true
      super
    end
238

239 240 241
    def down
      @went_down = true
      super
242
    end
243
  end
244

245 246 247 248
  def test_instance_based_migration_up
    migration = MockMigration.new
    assert !migration.went_up, 'have not gone up'
    assert !migration.went_down, 'have not gone down'
249

250 251 252 253
    migration.migrate :up
    assert migration.went_up, 'have gone up'
    assert !migration.went_down, 'have not gone down'
  end
254

255 256 257 258
  def test_instance_based_migration_down
    migration = MockMigration.new
    assert !migration.went_up, 'have not gone up'
    assert !migration.went_down, 'have not gone down'
259

260 261 262 263
    migration.migrate :down
    assert !migration.went_up, 'have gone up'
    assert migration.went_down, 'have not gone down'
  end
264

265 266 267 268
  if ActiveRecord::Base.connection.supports_ddl_transactions?
    def test_migrator_one_up_with_exception_and_rollback
      assert_no_column Person, :last_name

269
      migration = Class.new(ActiveRecord::Migration::Current) {
270 271 272 273 274 275
        def version; 100 end
        def migrate(x)
          add_column "people", "last_name", :string
          raise 'Something broke'
        end
      }.new
276

277
      migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
278

279
      e = assert_raise(StandardError) { migrator.migrate }
280

281
      assert_equal "An error has occurred, this and all later migrations canceled:\n\nSomething broke", e.message
282

283 284
      assert_no_column Person, :last_name,
        "On error, the Migrator should revert schema changes but it did not."
B
bondarev 已提交
285 286
    end

287 288
    def test_migrator_one_up_with_exception_and_rollback_using_run
      assert_no_column Person, :last_name
B
bondarev 已提交
289

290
      migration = Class.new(ActiveRecord::Migration::Current) {
291 292 293 294 295 296
        def version; 100 end
        def migrate(x)
          add_column "people", "last_name", :string
          raise 'Something broke'
        end
      }.new
B
bondarev 已提交
297

298
      migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
B
bondarev 已提交
299

300
      e = assert_raise(StandardError) { migrator.run }
B
bondarev 已提交
301

302
      assert_equal "An error has occurred, this and all later migrations canceled:\n\nSomething broke", e.message
B
bondarev 已提交
303

304 305
      assert_no_column Person, :last_name,
        "On error, the Migrator should revert schema changes but it did not."
306 307
    end

308 309
    def test_migration_without_transaction
      assert_no_column Person, :last_name
310

311
      migration = Class.new(ActiveRecord::Migration::Current) {
312
        self.disable_ddl_transaction!
313

314 315 316 317 318 319
        def version; 101 end
        def migrate(x)
          add_column "people", "last_name", :string
          raise 'Something broke'
        end
      }.new
320

321 322 323
      migrator = ActiveRecord::Migrator.new(:up, [migration], 101)
      e = assert_raise(StandardError) { migrator.migrate }
      assert_equal "An error has occurred, all later migrations canceled:\n\nSomething broke", e.message
324

325 326 327 328 329 330 331
      assert_column Person, :last_name,
        "without ddl transactions, the Migrator should not rollback on error but it did."
    ensure
      Person.reset_column_information
      if Person.column_names.include?('last_name')
        Person.connection.remove_column('people', 'last_name')
      end
332
    end
333
  end
334

335
  def test_schema_migrations_table_name
336 337 338
    original_schema_migrations_table_name = ActiveRecord::Migrator.schema_migrations_table_name

    assert_equal "schema_migrations", ActiveRecord::Migrator.schema_migrations_table_name
339 340 341 342
    ActiveRecord::Base.table_name_prefix = "prefix_"
    ActiveRecord::Base.table_name_suffix = "_suffix"
    Reminder.reset_table_name
    assert_equal "prefix_schema_migrations_suffix", ActiveRecord::Migrator.schema_migrations_table_name
343 344 345
    ActiveRecord::Base.schema_migrations_table_name = "changed"
    Reminder.reset_table_name
    assert_equal "prefix_changed_suffix", ActiveRecord::Migrator.schema_migrations_table_name
346 347 348
    ActiveRecord::Base.table_name_prefix = ""
    ActiveRecord::Base.table_name_suffix = ""
    Reminder.reset_table_name
349 350 351 352
    assert_equal "changed", ActiveRecord::Migrator.schema_migrations_table_name
  ensure
    ActiveRecord::Base.schema_migrations_table_name = original_schema_migrations_table_name
    Reminder.reset_table_name
353 354
  end

355 356 357
  def test_internal_metadata_table_name
    original_internal_metadata_table_name = ActiveRecord::Base.internal_metadata_table_name

358 359 360
    assert_equal "ar_internal_metadata", ActiveRecord::InternalMetadata.table_name
    ActiveRecord::Base.table_name_prefix = "p_"
    ActiveRecord::Base.table_name_suffix = "_s"
361
    Reminder.reset_table_name
362
    assert_equal "p_ar_internal_metadata_s", ActiveRecord::InternalMetadata.table_name
363 364
    ActiveRecord::Base.internal_metadata_table_name = "changed"
    Reminder.reset_table_name
365
    assert_equal "p_changed_s", ActiveRecord::InternalMetadata.table_name
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
    ActiveRecord::Base.table_name_prefix = ""
    ActiveRecord::Base.table_name_suffix = ""
    Reminder.reset_table_name
    assert_equal "changed", ActiveRecord::InternalMetadata.table_name
  ensure
    ActiveRecord::Base.internal_metadata_table_name = original_internal_metadata_table_name
    Reminder.reset_table_name
  end

  def test_internal_metadata_stores_environment
    current_env     = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
    migrations_path = MIGRATIONS_ROOT + "/valid"
    old_path        = ActiveRecord::Migrator.migrations_paths
    ActiveRecord::Migrator.migrations_paths = migrations_path

S
schneems 已提交
381
    ActiveRecord::Migrator.up(migrations_path)
382
    assert_equal current_env, ActiveRecord::InternalMetadata[:environment]
383 384 385 386 387 388 389 390 391 392

    original_rails_env  = ENV["RAILS_ENV"]
    original_rack_env   = ENV["RACK_ENV"]
    ENV["RAILS_ENV"]    = ENV["RACK_ENV"] = "foofoo"
    new_env     = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call

    refute_equal current_env, new_env

    sleep 1 # mysql by default does not store fractional seconds in the database
    ActiveRecord::Migrator.up(migrations_path)
393
    assert_equal new_env, ActiveRecord::InternalMetadata[:environment]
394 395 396 397 398 399
  ensure
    ActiveRecord::Migrator.migrations_paths = old_path
    ENV["RAILS_ENV"] = original_rails_env
    ENV["RACK_ENV"]  = original_rack_env
  end

400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426

  def test_migration_sets_internal_metadata_even_when_fully_migrated
    current_env     = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
    migrations_path = MIGRATIONS_ROOT + "/valid"
    old_path        = ActiveRecord::Migrator.migrations_paths
    ActiveRecord::Migrator.migrations_paths = migrations_path

    ActiveRecord::Migrator.up(migrations_path)
    assert_equal current_env, ActiveRecord::InternalMetadata[:environment]

    original_rails_env  = ENV["RAILS_ENV"]
    original_rack_env   = ENV["RACK_ENV"]
    ENV["RAILS_ENV"]    = ENV["RACK_ENV"] = "foofoo"
    new_env     = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call

    refute_equal current_env, new_env

    sleep 1 # mysql by default does not store fractional seconds in the database

    ActiveRecord::Migrator.up(migrations_path)
    assert_equal new_env, ActiveRecord::InternalMetadata[:environment]
  ensure
    ActiveRecord::Migrator.migrations_paths = old_path
    ENV["RAILS_ENV"] = original_rails_env
    ENV["RACK_ENV"]  = original_rack_env
  end

427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
  def test_rename_internal_metadata_table
    original_internal_metadata_table_name = ActiveRecord::Base.internal_metadata_table_name

    ActiveRecord::Base.internal_metadata_table_name = "active_record_internal_metadatas"
    Reminder.reset_table_name

    ActiveRecord::Base.internal_metadata_table_name = original_internal_metadata_table_name
    Reminder.reset_table_name

    assert_equal "ar_internal_metadata", ActiveRecord::InternalMetadata.table_name
  ensure
    ActiveRecord::Base.internal_metadata_table_name = original_internal_metadata_table_name
    Reminder.reset_table_name
  end

442
  def test_proper_table_name_on_migration
443
    reminder_class = new_isolated_reminder_class
444 445 446
    migration = ActiveRecord::Migration.new
    assert_equal "table", migration.proper_table_name('table')
    assert_equal "table", migration.proper_table_name(:table)
447 448 449
    assert_equal "reminders", migration.proper_table_name(reminder_class)
    reminder_class.reset_table_name
    assert_equal reminder_class.table_name, migration.proper_table_name(reminder_class)
450 451 452 453

    # Use the model's own prefix/suffix if a model is given
    ActiveRecord::Base.table_name_prefix = "ARprefix_"
    ActiveRecord::Base.table_name_suffix = "_ARsuffix"
454 455 456 457 458 459 460
    reminder_class.table_name_prefix = 'prefix_'
    reminder_class.table_name_suffix = '_suffix'
    reminder_class.reset_table_name
    assert_equal "prefix_reminders_suffix", migration.proper_table_name(reminder_class)
    reminder_class.table_name_prefix = ''
    reminder_class.table_name_suffix = ''
    reminder_class.reset_table_name
461 462 463 464

    # Use AR::Base's prefix/suffix if string or symbol is given
    ActiveRecord::Base.table_name_prefix = "prefix_"
    ActiveRecord::Base.table_name_suffix = "_suffix"
465
    reminder_class.reset_table_name
466 467 468 469
    assert_equal "prefix_table_suffix", migration.proper_table_name('table', migration.table_name_options)
    assert_equal "prefix_table_suffix", migration.proper_table_name(:table, migration.table_name_options)
  end

470 471
  def test_rename_table_with_prefix_and_suffix
    assert !Thing.table_exists?
472 473
    ActiveRecord::Base.table_name_prefix = 'p_'
    ActiveRecord::Base.table_name_suffix = '_s'
474 475 476
    Thing.reset_table_name
    Thing.reset_sequence_name
    WeNeedThings.up
477
    Thing.reset_column_information
478 479

    assert Thing.create("content" => "hello world")
480
    assert_equal "hello world", Thing.first.content
481 482

    RenameThings.up
483
    Thing.table_name = "p_awesome_things_s"
484

485
    assert_equal "hello world", Thing.first.content
486 487 488 489 490 491 492 493 494 495 496
  ensure
    Thing.reset_table_name
    Thing.reset_sequence_name
  end

  def test_add_drop_table_with_prefix_and_suffix
    assert !Reminder.table_exists?
    ActiveRecord::Base.table_name_prefix = 'prefix_'
    ActiveRecord::Base.table_name_suffix = '_suffix'
    Reminder.reset_table_name
    Reminder.reset_sequence_name
497
    Reminder.reset_column_information
498 499
    WeNeedReminders.up
    assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
500
    assert_equal "hello world", Reminder.first.content
501 502

    WeNeedReminders.down
503
    assert_raise(ActiveRecord::StatementInvalid) { Reminder.first }
504 505 506
  ensure
    Reminder.reset_sequence_name
  end
507

508 509 510
  def test_create_table_with_binary_column
    assert_nothing_raised {
      Person.connection.create_table :binary_testings do |t|
511
        t.column "data", :binary, :null => false
512
      end
513 514 515 516
    }

    columns = Person.connection.columns(:binary_testings)
    data_column = columns.detect { |c| c.name == "data" }
517

J
Jon Leighton 已提交
518
    assert_nil data_column.default
519
  ensure
520
    Person.connection.drop_table :binary_testings, if_exists: true
521 522
  end

523 524 525
  unless mysql_enforcing_gtid_consistency?
    def test_create_table_with_query
      Person.connection.create_table(:person, force: true)
526

527
      Person.connection.create_table :table_from_query_testings, as: "SELECT id FROM person"
528

529 530 531
      columns = Person.connection.columns(:table_from_query_testings)
      assert_equal 1, columns.length
      assert_equal "id", columns.first.name
532
    ensure
533 534
      Person.connection.drop_table :table_from_query_testings rescue nil
    end
535

536 537
    def test_create_table_with_query_from_relation
      Person.connection.create_table(:person, force: true)
538

539
      Person.connection.create_table :table_from_query_testings, as: Person.select(:id)
540

541 542 543
      columns = Person.connection.columns(:table_from_query_testings)
      assert_equal 1, columns.length
      assert_equal "id", columns.first.name
544
    ensure
545 546
      Person.connection.drop_table :table_from_query_testings rescue nil
    end
547 548
  end

549 550 551 552 553 554 555 556 557 558 559
  if current_adapter? :OracleAdapter
    def test_create_table_with_custom_sequence_name
      # table name is 29 chars, the standard sequence name will
      # be 33 chars and should be shortened
      assert_nothing_raised do
        begin
          Person.connection.create_table :table_with_name_thats_just_ok do |t|
            t.column :foo, :string, :null => false
          end
        ensure
          Person.connection.drop_table :table_with_name_thats_just_ok rescue nil
560 561 562
        end
      end

563 564 565 566 567 568 569
      # should be all good w/ a custom sequence name
      assert_nothing_raised do
        begin
          Person.connection.create_table :table_with_name_thats_just_ok,
            :sequence_name => 'suitably_short_seq' do |t|
            t.column :foo, :string, :null => false
          end
570

571
          Person.connection.execute("select suitably_short_seq.nextval from dual")
572

573 574 575 576
        ensure
          Person.connection.drop_table :table_with_name_thats_just_ok,
            :sequence_name => 'suitably_short_seq' rescue nil
        end
577 578
      end

579 580 581 582
      # confirm the custom sequence got dropped
      assert_raise(ActiveRecord::StatementInvalid) do
        Person.connection.execute("select suitably_short_seq.nextval from dual")
      end
583
    end
584
  end
585

A
Abdelkader Boudih 已提交
586
  if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter)
587
    def test_out_of_range_integer_limit_should_raise
F
Franky W 已提交
588
      e = assert_raise(ActiveRecord::ActiveRecordError, "integer limit didn't raise") do
589 590 591
        Person.connection.create_table :test_integer_limits, :force => true do |t|
          t.column :bigone, :integer, :limit => 10
        end
592 593
      end

F
Franky W 已提交
594
      assert_match(/No integer type has byte size 10/, e.message)
595 596 597 598
    ensure
      Person.connection.drop_table :test_integer_limits, if_exists: true
    end
  end
F
Franky W 已提交
599

600 601 602 603 604
  if current_adapter?(:Mysql2Adapter)
    def test_out_of_range_text_limit_should_raise
      e = assert_raise(ActiveRecord::ActiveRecordError, "text limit didn't raise") do
        Person.connection.create_table :test_text_limits, force: true do |t|
          t.text :bigtext, limit: 0xfffffffff
605 606
        end
      end
607 608

      assert_match(/No text type has byte length #{0xfffffffff}/, e.message)
609
    ensure
610
      Person.connection.drop_table :test_text_limits, if_exists: true
611
    end
612 613
  end

614
  if ActiveRecord::Base.connection.supports_advisory_locks?
615
    def test_migrator_generates_valid_lock_id
616
      migration = Class.new(ActiveRecord::Migration::Current).new
617 618
      migrator = ActiveRecord::Migrator.new(:up, [migration], 100)

619
      lock_id = migrator.send(:generate_migrator_advisory_lock_id)
620

621 622 623 624
      assert ActiveRecord::Base.connection.get_advisory_lock(lock_id),
        "the Migrator should have generated a valid lock id, but it didn't"
      assert ActiveRecord::Base.connection.release_advisory_lock(lock_id),
        "the Migrator should have generated a valid lock id, but it didn't"
625 626
    end

627
    def test_generate_migrator_advisory_lock_id
628 629
      # It is important we are consistent with how we generate this so that
      # exclusive locking works across migrator versions
630
      migration = Class.new(ActiveRecord::Migration::Current).new
631 632
      migrator = ActiveRecord::Migrator.new(:up, [migration], 100)

633
      lock_id = migrator.send(:generate_migrator_advisory_lock_id)
634 635 636

      current_database = ActiveRecord::Base.connection.current_database
      salt = ActiveRecord::Migrator::MIGRATOR_SALT
637
      expected_id = Zlib.crc32(current_database) * salt
638

639 640
      assert lock_id == expected_id, "expected lock id generated by the migrator to be #{expected_id}, but it was #{lock_id} instead"
      assert lock_id.bit_length <= 63, "lock id must be a signed integer of max 63 bits magnitude"
641 642 643 644 645
    end

    def test_migrator_one_up_with_unavailable_lock
      assert_no_column Person, :last_name

646
      migration = Class.new(ActiveRecord::Migration::Current) {
647 648 649 650 651 652 653
        def version; 100 end
        def migrate(x)
          add_column "people", "last_name", :string
        end
      }.new

      migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
654
      lock_id = migrator.send(:generate_migrator_advisory_lock_id)
655

656
      with_another_process_holding_lock(lock_id) do
657 658 659 660 661 662 663 664 665 666
        assert_raise(ActiveRecord::ConcurrentMigrationError) { migrator.migrate }
      end

      assert_no_column Person, :last_name,
        "without an advisory lock, the Migrator should not make any changes, but it did."
    end

    def test_migrator_one_up_with_unavailable_lock_using_run
      assert_no_column Person, :last_name

667
      migration = Class.new(ActiveRecord::Migration::Current) {
668 669 670 671 672 673 674
        def version; 100 end
        def migrate(x)
          add_column "people", "last_name", :string
        end
      }.new

      migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
675
      lock_id = migrator.send(:generate_migrator_advisory_lock_id)
676

677
      with_another_process_holding_lock(lock_id) do
678 679 680 681 682 683 684 685
        assert_raise(ActiveRecord::ConcurrentMigrationError) { migrator.run }
      end

      assert_no_column Person, :last_name,
        "without an advisory lock, the Migrator should not make any changes, but it did."
    end
  end

686
  protected
687 688 689 690 691 692 693 694
    # This is needed to isolate class_attribute assignments like `table_name_prefix`
    # for each test case.
    def new_isolated_reminder_class
      Class.new(Reminder) {
        def self.name; "Reminder"; end
        def self.base_class; self; end
      }
    end
695

696
    def with_another_process_holding_lock(lock_id)
A
Aaron Patterson 已提交
697 698
      thread_lock = Concurrent::CountDownLatch.new
      test_terminated = Concurrent::CountDownLatch.new
699 700 701 702

      other_process = Thread.new do
        begin
          conn = ActiveRecord::Base.connection_pool.checkout
703
          conn.get_advisory_lock(lock_id)
A
Aaron Patterson 已提交
704 705
          thread_lock.count_down
          test_terminated.wait # hold the lock open until we tested everything
706
        ensure
707
          conn.release_advisory_lock(lock_id)
708 709 710 711
          ActiveRecord::Base.connection_pool.checkin(conn)
        end
      end

A
Aaron Patterson 已提交
712
      thread_lock.wait # wait until the 'other process' has the lock
713 714 715

      yield

A
Aaron Patterson 已提交
716
      test_terminated.count_down
717 718
      other_process.join
    end
719
end
720

721 722 723 724 725
class ReservedWordsMigrationTest < ActiveRecord::TestCase
  def test_drop_index_from_table_named_values
    connection = Person.connection
    connection.create_table :values, :force => true do |t|
      t.integer :value
726
    end
727

728 729 730
    assert_nothing_raised do
      connection.add_index :values, :value
      connection.remove_index :values, :column => :value
731
    end
732
  ensure
733 734 735 736
    connection.drop_table :values rescue nil
  end
end

737 738 739 740 741 742 743
class ExplicitlyNamedIndexMigrationTest < ActiveRecord::TestCase
  def test_drop_index_by_name
    connection = Person.connection
    connection.create_table :values, force: true do |t|
      t.integer :value
    end

744
    assert_nothing_raised do
745 746 747
      connection.add_index :values, :value, name: 'a_different_name'
      connection.remove_index :values, column: :value, name: 'a_different_name'
    end
748
  ensure
749 750 751 752
    connection.drop_table :values rescue nil
  end
end

753 754 755 756 757
if ActiveRecord::Base.connection.supports_bulk_alter?
  class BulkAlterTableMigrationsTest < ActiveRecord::TestCase
    def setup
      @connection = Person.connection
      @connection.create_table(:delete_me, :force => true) {|t| }
A
Akira Matsuda 已提交
758 759
      Person.reset_column_information
      Person.reset_sequence_name
760
    end
761

G
Guo Xiang Tan 已提交
762
    teardown do
763 764
      Person.connection.drop_table(:delete_me) rescue nil
    end
765

766 767
    def test_adding_multiple_columns
      assert_queries(1) do
768
        with_bulk_change_table do |t|
769
          t.column :name, :string
770
          t.string :qualification, :experience
771 772
          t.integer :age, :default => 0
          t.date :birthdate
773
          t.timestamps null: true
774
        end
775
      end
776

777 778
      assert_equal 8, columns.size
      [:name, :qualification, :experience].each {|s| assert_equal :string, column(s).type }
779
      assert_equal '0', column(:age).default
780
    end
781

782 783 784
    def test_removing_columns
      with_bulk_change_table do |t|
        t.string :qualification, :experience
785 786
      end

787
      [:qualification, :experience].each {|c| assert column(c) }
788

789 790 791 792
      assert_queries(1) do
        with_bulk_change_table do |t|
          t.remove :qualification, :experience
          t.string :qualification_experience
793
        end
794
      end
795

796 797 798
      [:qualification, :experience].each {|c| assert ! column(c) }
      assert column(:qualification_experience)
    end
799

800 801 802 803 804
    def test_adding_indexes
      with_bulk_change_table do |t|
        t.string :username
        t.string :name
        t.integer :age
805 806
      end

807 808
      # Adding an index fires a query every time to check if an index already exists or not
      assert_queries(3) do
809
        with_bulk_change_table do |t|
810 811
          t.index :username, :unique => true, :name => :awesome_username_index
          t.index [:name, :age]
812
        end
813
      end
814

815
      assert_equal 2, indexes.size
816

817 818 819
      name_age_index = index(:index_delete_me_on_name_and_age)
      assert_equal ['name', 'age'].sort, name_age_index.columns.sort
      assert ! name_age_index.unique
820

821 822
      assert index(:awesome_username_index).unique
    end
823

824 825 826 827
    def test_removing_index
      with_bulk_change_table do |t|
        t.string :name
        t.index :name
828
      end
829

830 831 832
      assert index(:index_delete_me_on_name)

      assert_queries(3) do
833
        with_bulk_change_table do |t|
834 835
          t.remove_index :name
          t.index :name, :name => :new_name_index, :unique => true
836
        end
837
      end
838

839
      assert ! index(:index_delete_me_on_name)
840

841 842 843
      new_name_index = index(:new_name_index)
      assert new_name_index.unique
    end
844

845 846 847 848
    def test_changing_columns
      with_bulk_change_table do |t|
        t.string :name
        t.date :birthdate
849
      end
850

851 852
      assert ! column(:name).default
      assert_equal :date, column(:birthdate).type
853

854 855 856
      # One query for columns (delete_me table)
      # One query for primary key (delete_me table)
      # One query to do the bulk change
857
      assert_queries(3, :ignore_none => true) do
858 859 860
        with_bulk_change_table do |t|
          t.change :name, :string, :default => 'NONAME'
          t.change :birthdate, :datetime
861
        end
862 863
      end

864 865 866
      assert_equal 'NONAME', column(:name).default
      assert_equal :datetime, column(:birthdate).type
    end
867

868
    protected
869

870 871 872
    def with_bulk_change_table
      # Reset columns/indexes cache as we're changing the table
      @columns = @indexes = nil
873

874 875
      Person.connection.change_table(:delete_me, :bulk => true) do |t|
        yield t
876
      end
877
    end
878

879 880 881
    def column(name)
      columns.detect {|c| c.name == name.to_s }
    end
882

883 884
    def columns
      @columns ||= Person.connection.columns('delete_me')
885 886
    end

887 888
    def index(name)
      indexes.detect {|i| i.name == name.to_s }
889 890
    end

891 892 893 894 895 896 897 898
    def indexes
      @indexes ||= Person.connection.indexes('delete_me')
    end
  end # AlterTableMigrationsTest

end

class CopyMigrationsTest < ActiveRecord::TestCase
899 900
  include ActiveSupport::Testing::Stream

901 902 903 904 905 906 907 908 909 910 911 912 913 914 915
  def setup
  end

  def clear
    ActiveRecord::Base.timestamped_migrations = true
    to_delete = Dir[@migrations_path + "/*.rb"] - @existing_migrations
    File.delete(*to_delete)
  end

  def test_copying_migrations_without_timestamps
    ActiveRecord::Base.timestamped_migrations = false
    @migrations_path = MIGRATIONS_ROOT + "/valid"
    @existing_migrations = Dir[@migrations_path + "/*.rb"]

    copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy"})
A
Arun Agrawal 已提交
916 917
    assert File.exist?(@migrations_path + "/4_people_have_hobbies.bukkits.rb")
    assert File.exist?(@migrations_path + "/5_people_have_descriptions.bukkits.rb")
918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935
    assert_equal [@migrations_path + "/4_people_have_hobbies.bukkits.rb", @migrations_path + "/5_people_have_descriptions.bukkits.rb"], copied.map(&:filename)

    expected = "# This migration comes from bukkits (originally 1)"
    assert_equal expected, IO.readlines(@migrations_path + "/4_people_have_hobbies.bukkits.rb")[0].chomp

    files_count = Dir[@migrations_path + "/*.rb"].length
    copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy"})
    assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
    assert copied.empty?
  ensure
    clear
  end

  def test_copying_migrations_without_timestamps_from_2_sources
    ActiveRecord::Base.timestamped_migrations = false
    @migrations_path = MIGRATIONS_ROOT + "/valid"
    @existing_migrations = Dir[@migrations_path + "/*.rb"]

936
    sources = {}
937 938 939
    sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy"
    sources[:omg] = MIGRATIONS_ROOT + "/to_copy2"
    ActiveRecord::Migration.copy(@migrations_path, sources)
A
Arun Agrawal 已提交
940 941 942 943
    assert File.exist?(@migrations_path + "/4_people_have_hobbies.bukkits.rb")
    assert File.exist?(@migrations_path + "/5_people_have_descriptions.bukkits.rb")
    assert File.exist?(@migrations_path + "/6_create_articles.omg.rb")
    assert File.exist?(@migrations_path + "/7_create_comments.omg.rb")
944 945 946 947 948 949 950

    files_count = Dir[@migrations_path + "/*.rb"].length
    ActiveRecord::Migration.copy(@migrations_path, sources)
    assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
  ensure
    clear
  end
951

952 953 954
  def test_copying_migrations_with_timestamps
    @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
    @existing_migrations = Dir[@migrations_path + "/*.rb"]
955

956
    travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
957
      copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
A
Arun Agrawal 已提交
958 959
      assert File.exist?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
      assert File.exist?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
960 961 962
      expected = [@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb",
                  @migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb"]
      assert_equal expected, copied.map(&:filename)
963

964
      files_count = Dir[@migrations_path + "/*.rb"].length
965
      copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
966 967 968
      assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
      assert copied.empty?
    end
969 970 971
  ensure
    clear
  end
972

973 974 975
  def test_copying_migrations_with_timestamps_from_2_sources
    @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
    @existing_migrations = Dir[@migrations_path + "/*.rb"]
976

977
    sources = {}
978 979 980
    sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
    sources[:omg]     = MIGRATIONS_ROOT + "/to_copy_with_timestamps2"

981
    travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
982
      copied = ActiveRecord::Migration.copy(@migrations_path, sources)
A
Arun Agrawal 已提交
983 984 985 986
      assert File.exist?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
      assert File.exist?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
      assert File.exist?(@migrations_path + "/20100726101012_create_articles.omg.rb")
      assert File.exist?(@migrations_path + "/20100726101013_create_comments.omg.rb")
987
      assert_equal 4, copied.length
988 989 990 991 992

      files_count = Dir[@migrations_path + "/*.rb"].length
      ActiveRecord::Migration.copy(@migrations_path, sources)
      assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
    end
993 994 995
  ensure
    clear
  end
996

997 998 999
  def test_copying_migrations_with_timestamps_to_destination_with_timestamps_in_future
    @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
    @existing_migrations = Dir[@migrations_path + "/*.rb"]
1000

1001
    travel_to(Time.utc(2010, 2, 20, 10, 10, 10)) do
1002
      ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
A
Arun Agrawal 已提交
1003 1004
      assert File.exist?(@migrations_path + "/20100301010102_people_have_hobbies.bukkits.rb")
      assert File.exist?(@migrations_path + "/20100301010103_people_have_descriptions.bukkits.rb")
1005

1006 1007 1008 1009
      files_count = Dir[@migrations_path + "/*.rb"].length
      copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
      assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
      assert copied.empty?
1010
    end
1011 1012 1013
  ensure
    clear
  end
1014

1015 1016 1017 1018 1019 1020
  def test_copying_migrations_preserving_magic_comments
    ActiveRecord::Base.timestamped_migrations = false
    @migrations_path = MIGRATIONS_ROOT + "/valid"
    @existing_migrations = Dir[@migrations_path + "/*.rb"]

    copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/magic"})
A
Arun Agrawal 已提交
1021
    assert File.exist?(@migrations_path + "/4_currencies_have_symbols.bukkits.rb")
1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034
    assert_equal [@migrations_path + "/4_currencies_have_symbols.bukkits.rb"], copied.map(&:filename)

    expected = "# coding: ISO-8859-15\n# This migration comes from bukkits (originally 1)"
    assert_equal expected, IO.readlines(@migrations_path + "/4_currencies_have_symbols.bukkits.rb")[0..1].join.chomp

    files_count = Dir[@migrations_path + "/*.rb"].length
    copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/magic"})
    assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
    assert copied.empty?
  ensure
    clear
  end

1035 1036 1037
  def test_skipping_migrations
    @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
    @existing_migrations = Dir[@migrations_path + "/*.rb"]
1038 1039

    sources = {}
1040 1041
    sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
    sources[:omg]     = MIGRATIONS_ROOT + "/to_copy_with_name_collision"
1042

1043 1044 1045 1046
    skipped = []
    on_skip = Proc.new { |name, migration| skipped << "#{name} #{migration.name}" }
    copied = ActiveRecord::Migration.copy(@migrations_path, sources, :on_skip => on_skip)
    assert_equal 2, copied.length
1047

1048 1049 1050 1051 1052
    assert_equal 1, skipped.length
    assert_equal ["omg PeopleHaveHobbies"], skipped
  ensure
    clear
  end
1053

1054 1055 1056
  def test_skip_is_not_called_if_migrations_are_from_the_same_plugin
    @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
    @existing_migrations = Dir[@migrations_path + "/*.rb"]
1057

1058
    sources = {}
1059
    sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
1060

1061 1062 1063 1064
    skipped = []
    on_skip = Proc.new { |name, migration| skipped << "#{name} #{migration.name}" }
    copied = ActiveRecord::Migration.copy(@migrations_path, sources, :on_skip => on_skip)
    ActiveRecord::Migration.copy(@migrations_path, sources, :on_skip => on_skip)
1065

1066 1067 1068 1069 1070
    assert_equal 2, copied.length
    assert_equal 0, skipped.length
  ensure
    clear
  end
1071

1072 1073 1074
  def test_copying_migrations_to_non_existing_directory
    @migrations_path = MIGRATIONS_ROOT + "/non_existing"
    @existing_migrations = []
1075

1076
    travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
1077
      copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
A
Arun Agrawal 已提交
1078 1079
      assert File.exist?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
      assert File.exist?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
1080 1081
      assert_equal 2, copied.length
    end
1082 1083 1084 1085
  ensure
    clear
    Dir.delete(@migrations_path)
  end
1086

1087 1088 1089
  def test_copying_migrations_to_empty_directory
    @migrations_path = MIGRATIONS_ROOT + "/empty"
    @existing_migrations = []
1090

1091
    travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
1092
      copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
A
Arun Agrawal 已提交
1093 1094
      assert File.exist?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
      assert File.exist?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
1095
      assert_equal 2, copied.length
1096
    end
1097 1098
  ensure
    clear
1099
  end
1100 1101 1102 1103 1104 1105 1106 1107 1108

  def test_check_pending_with_stdlib_logger
    old, ActiveRecord::Base.logger = ActiveRecord::Base.logger, ::Logger.new($stdout)
    quietly do
      assert_nothing_raised { ActiveRecord::Migration::CheckPending.new(Proc.new {}).call({}) }
    end
  ensure
    ActiveRecord::Base.logger = old
  end
1109

1110
end