mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
97347d8c40
We as Arm Treasure Data are using Optimizer Hints with a monkey patch (https://gist.github.com/kamipo/4c8539f0ce4acf85075cf5a6b0d9712e), especially in order to use `MAX_EXECUTION_TIME` (refer #31129). Example: ```ruby class Job < ApplicationRecord default_scope { optimizer_hints("MAX_EXECUTION_TIME(50000) NO_INDEX_MERGE(jobs)") } end ``` Optimizer Hints is supported not only for MySQL but also for most databases (PostgreSQL on RDS, Oracle, SQL Server, etc), it is really helpful to turn heavy queries for large scale applications.
427 lines
13 KiB
Ruby
427 lines
13 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "cases/helper"
|
|
|
|
class Horse < ActiveRecord::Base
|
|
end
|
|
|
|
module ActiveRecord
|
|
class InvertibleMigrationTest < ActiveRecord::TestCase
|
|
class SilentMigration < ActiveRecord::Migration::Current
|
|
def write(text = "")
|
|
# sssshhhhh!!
|
|
end
|
|
end
|
|
|
|
class InvertibleMigration < SilentMigration
|
|
def change
|
|
create_table("horses") do |t|
|
|
t.column :content, :text
|
|
t.column :remind_at, :datetime
|
|
end
|
|
end
|
|
end
|
|
|
|
class InvertibleTransactionMigration < InvertibleMigration
|
|
def change
|
|
transaction do
|
|
super
|
|
end
|
|
end
|
|
end
|
|
|
|
class InvertibleRevertMigration < SilentMigration
|
|
def change
|
|
revert do
|
|
create_table("horses") do |t|
|
|
t.column :content, :text
|
|
t.column :remind_at, :datetime
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
class InvertibleByPartsMigration < SilentMigration
|
|
attr_writer :test
|
|
def change
|
|
create_table("new_horses") do |t|
|
|
t.column :breed, :string
|
|
end
|
|
reversible do |dir|
|
|
@test.yield :both
|
|
dir.up { @test.yield :up }
|
|
dir.down { @test.yield :down }
|
|
end
|
|
revert do
|
|
create_table("horses") do |t|
|
|
t.column :content, :text
|
|
t.column :remind_at, :datetime
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
class NonInvertibleMigration < SilentMigration
|
|
def change
|
|
create_table("horses") do |t|
|
|
t.column :content, :text
|
|
t.column :remind_at, :datetime
|
|
end
|
|
remove_column "horses", :content
|
|
end
|
|
end
|
|
|
|
class RemoveIndexMigration1 < SilentMigration
|
|
def self.up
|
|
create_table("horses") do |t|
|
|
t.column :name, :string
|
|
t.column :color, :string
|
|
t.index [:name, :color]
|
|
end
|
|
end
|
|
end
|
|
|
|
class RemoveIndexMigration2 < SilentMigration
|
|
def change
|
|
change_table("horses") do |t|
|
|
t.remove_index [:name, :color]
|
|
end
|
|
end
|
|
end
|
|
|
|
class ChangeColumnDefault1 < SilentMigration
|
|
def change
|
|
create_table("horses") do |t|
|
|
t.column :name, :string, default: "Sekitoba"
|
|
end
|
|
end
|
|
end
|
|
|
|
class ChangeColumnDefault2 < SilentMigration
|
|
def change
|
|
change_column_default :horses, :name, from: "Sekitoba", to: "Diomed"
|
|
end
|
|
end
|
|
|
|
class DisableExtension1 < SilentMigration
|
|
def change
|
|
enable_extension "hstore"
|
|
end
|
|
end
|
|
|
|
class DisableExtension2 < SilentMigration
|
|
def change
|
|
disable_extension "hstore"
|
|
end
|
|
end
|
|
|
|
class LegacyMigration < ActiveRecord::Migration::Current
|
|
def self.up
|
|
create_table("horses") do |t|
|
|
t.column :content, :text
|
|
t.column :remind_at, :datetime
|
|
end
|
|
end
|
|
|
|
def self.down
|
|
drop_table("horses")
|
|
end
|
|
end
|
|
|
|
class RevertWholeMigration < SilentMigration
|
|
def initialize(name = self.class.name, version = nil, migration)
|
|
@migration = migration
|
|
super(name, version)
|
|
end
|
|
|
|
def change
|
|
revert @migration
|
|
end
|
|
end
|
|
|
|
class NestedRevertWholeMigration < RevertWholeMigration
|
|
def change
|
|
revert { super }
|
|
end
|
|
end
|
|
|
|
class RevertNamedIndexMigration1 < SilentMigration
|
|
def change
|
|
create_table("horses") do |t|
|
|
t.column :content, :string
|
|
t.column :remind_at, :datetime
|
|
end
|
|
add_index :horses, :content
|
|
end
|
|
end
|
|
|
|
class RevertNamedIndexMigration2 < SilentMigration
|
|
def change
|
|
add_index :horses, :content, name: "horses_index_named"
|
|
end
|
|
end
|
|
|
|
class RevertCustomForeignKeyTable < SilentMigration
|
|
def change
|
|
change_table(:horses) do |t|
|
|
t.references :owner, foreign_key: { to_table: :developers }
|
|
end
|
|
end
|
|
end
|
|
|
|
class UpOnlyMigration < SilentMigration
|
|
def change
|
|
add_column :horses, :oldie, :integer, default: 0
|
|
up_only { execute "update horses set oldie = 1" }
|
|
end
|
|
end
|
|
|
|
self.use_transactional_tests = false
|
|
|
|
setup do
|
|
@verbose_was, ActiveRecord::Migration.verbose = ActiveRecord::Migration.verbose, false
|
|
end
|
|
|
|
teardown do
|
|
%w[horses new_horses].each do |table|
|
|
if ActiveRecord::Base.connection.table_exists?(table)
|
|
ActiveRecord::Base.connection.drop_table(table)
|
|
end
|
|
end
|
|
ActiveRecord::Migration.verbose = @verbose_was
|
|
end
|
|
|
|
def test_no_reverse
|
|
migration = NonInvertibleMigration.new
|
|
migration.migrate(:up)
|
|
assert_raises(IrreversibleMigration) do
|
|
migration.migrate(:down)
|
|
end
|
|
end
|
|
|
|
def test_exception_on_removing_index_without_column_option
|
|
index_definition = ["horses", [:name, :color]]
|
|
migration1 = RemoveIndexMigration1.new
|
|
migration1.migrate(:up)
|
|
assert migration1.connection.index_exists?(*index_definition)
|
|
|
|
migration2 = RemoveIndexMigration2.new
|
|
migration2.migrate(:up)
|
|
assert_not migration2.connection.index_exists?(*index_definition)
|
|
|
|
migration2.migrate(:down)
|
|
assert migration2.connection.index_exists?(*index_definition)
|
|
end
|
|
|
|
def test_migrate_up
|
|
migration = InvertibleMigration.new
|
|
migration.migrate(:up)
|
|
assert migration.connection.table_exists?("horses"), "horses should exist"
|
|
end
|
|
|
|
def test_migrate_down
|
|
migration = InvertibleMigration.new
|
|
migration.migrate :up
|
|
migration.migrate :down
|
|
assert_not migration.connection.table_exists?("horses")
|
|
end
|
|
|
|
def test_migrate_revert
|
|
migration = InvertibleMigration.new
|
|
revert = InvertibleRevertMigration.new
|
|
migration.migrate :up
|
|
revert.migrate :up
|
|
assert_not migration.connection.table_exists?("horses")
|
|
revert.migrate :down
|
|
assert migration.connection.table_exists?("horses")
|
|
migration.migrate :down
|
|
assert_not migration.connection.table_exists?("horses")
|
|
end
|
|
|
|
def test_migrate_revert_by_part
|
|
InvertibleMigration.new.migrate :up
|
|
received = []
|
|
migration = InvertibleByPartsMigration.new
|
|
migration.test = ->(dir) {
|
|
assert migration.connection.table_exists?("horses")
|
|
assert migration.connection.table_exists?("new_horses")
|
|
received << dir
|
|
}
|
|
migration.migrate :up
|
|
assert_equal [:both, :up], received
|
|
assert_not migration.connection.table_exists?("horses")
|
|
assert migration.connection.table_exists?("new_horses")
|
|
migration.migrate :down
|
|
assert_equal [:both, :up, :both, :down], received
|
|
assert migration.connection.table_exists?("horses")
|
|
assert_not migration.connection.table_exists?("new_horses")
|
|
end
|
|
|
|
def test_migrate_revert_whole_migration
|
|
migration = InvertibleMigration.new
|
|
[LegacyMigration, InvertibleMigration].each do |klass|
|
|
revert = RevertWholeMigration.new(klass)
|
|
migration.migrate :up
|
|
revert.migrate :up
|
|
assert_not migration.connection.table_exists?("horses")
|
|
revert.migrate :down
|
|
assert migration.connection.table_exists?("horses")
|
|
migration.migrate :down
|
|
assert_not migration.connection.table_exists?("horses")
|
|
end
|
|
end
|
|
|
|
def test_migrate_nested_revert_whole_migration
|
|
revert = NestedRevertWholeMigration.new(InvertibleRevertMigration)
|
|
revert.migrate :down
|
|
assert revert.connection.table_exists?("horses")
|
|
revert.migrate :up
|
|
assert_not revert.connection.table_exists?("horses")
|
|
end
|
|
|
|
def test_migrate_revert_transaction
|
|
migration = InvertibleTransactionMigration.new
|
|
migration.migrate :up
|
|
assert migration.connection.table_exists?("horses")
|
|
migration.migrate :down
|
|
assert_not migration.connection.table_exists?("horses")
|
|
end
|
|
|
|
def test_migrate_revert_change_column_default
|
|
migration1 = ChangeColumnDefault1.new
|
|
migration1.migrate(:up)
|
|
assert_equal "Sekitoba", Horse.new.name
|
|
|
|
migration2 = ChangeColumnDefault2.new
|
|
migration2.migrate(:up)
|
|
Horse.reset_column_information
|
|
assert_equal "Diomed", Horse.new.name
|
|
|
|
migration2.migrate(:down)
|
|
Horse.reset_column_information
|
|
assert_equal "Sekitoba", Horse.new.name
|
|
end
|
|
|
|
if current_adapter?(:PostgreSQLAdapter)
|
|
def test_migrate_enable_and_disable_extension
|
|
migration1 = InvertibleMigration.new
|
|
migration2 = DisableExtension1.new
|
|
migration3 = DisableExtension2.new
|
|
|
|
assert_equal true, Horse.connection.extension_available?("hstore")
|
|
|
|
migration1.migrate(:up)
|
|
migration2.migrate(:up)
|
|
assert_equal true, Horse.connection.extension_enabled?("hstore")
|
|
|
|
migration3.migrate(:up)
|
|
assert_equal false, Horse.connection.extension_enabled?("hstore")
|
|
|
|
migration3.migrate(:down)
|
|
assert_equal true, Horse.connection.extension_enabled?("hstore")
|
|
|
|
migration2.migrate(:down)
|
|
assert_equal false, Horse.connection.extension_enabled?("hstore")
|
|
ensure
|
|
enable_extension!("hstore", ActiveRecord::Base.connection)
|
|
end
|
|
end
|
|
|
|
def test_revert_order
|
|
block = Proc.new { |t| t.string :name }
|
|
recorder = ActiveRecord::Migration::CommandRecorder.new(ActiveRecord::Base.connection)
|
|
recorder.instance_eval do
|
|
create_table("apples", &block)
|
|
revert do
|
|
create_table("bananas", &block)
|
|
revert do
|
|
create_table("clementines")
|
|
create_table("dates")
|
|
end
|
|
create_table("elderberries")
|
|
end
|
|
revert do
|
|
create_table("figs")
|
|
create_table("grapes")
|
|
end
|
|
end
|
|
assert_equal [[:create_table, ["apples"], block], [:drop_table, ["elderberries"], nil],
|
|
[:create_table, ["clementines"], nil], [:create_table, ["dates"], nil],
|
|
[:drop_table, ["bananas"], block], [:drop_table, ["grapes"], nil],
|
|
[:drop_table, ["figs"], nil]], recorder.commands
|
|
end
|
|
|
|
def test_legacy_up
|
|
LegacyMigration.migrate :up
|
|
assert ActiveRecord::Base.connection.table_exists?("horses"), "horses should exist"
|
|
end
|
|
|
|
def test_legacy_down
|
|
LegacyMigration.migrate :up
|
|
LegacyMigration.migrate :down
|
|
assert_not ActiveRecord::Base.connection.table_exists?("horses"), "horses should not exist"
|
|
end
|
|
|
|
def test_up
|
|
LegacyMigration.up
|
|
assert ActiveRecord::Base.connection.table_exists?("horses"), "horses should exist"
|
|
end
|
|
|
|
def test_down
|
|
LegacyMigration.up
|
|
LegacyMigration.down
|
|
assert_not ActiveRecord::Base.connection.table_exists?("horses"), "horses should not exist"
|
|
end
|
|
|
|
def test_migrate_down_with_table_name_prefix
|
|
ActiveRecord::Base.table_name_prefix = "p_"
|
|
ActiveRecord::Base.table_name_suffix = "_s"
|
|
migration = InvertibleMigration.new
|
|
migration.migrate(:up)
|
|
assert_nothing_raised { migration.migrate(:down) }
|
|
assert_not ActiveRecord::Base.connection.table_exists?("p_horses_s"), "p_horses_s should not exist"
|
|
ensure
|
|
ActiveRecord::Base.table_name_prefix = ActiveRecord::Base.table_name_suffix = ""
|
|
end
|
|
|
|
def test_migrations_can_handle_foreign_keys_to_specific_tables
|
|
migration = RevertCustomForeignKeyTable.new
|
|
InvertibleMigration.migrate(:up)
|
|
migration.migrate(:up)
|
|
migration.migrate(:down)
|
|
end
|
|
|
|
# MySQL 5.7 and Oracle do not allow to create duplicate indexes on the same columns
|
|
unless current_adapter?(:Mysql2Adapter, :OracleAdapter)
|
|
def test_migrate_revert_add_index_with_name
|
|
RevertNamedIndexMigration1.new.migrate(:up)
|
|
RevertNamedIndexMigration2.new.migrate(:up)
|
|
RevertNamedIndexMigration2.new.migrate(:down)
|
|
|
|
connection = ActiveRecord::Base.connection
|
|
assert connection.index_exists?(:horses, :content),
|
|
"index on content should exist"
|
|
assert_not connection.index_exists?(:horses, :content, name: "horses_index_named"),
|
|
"horses_index_named index should not exist"
|
|
end
|
|
end
|
|
|
|
def test_up_only
|
|
InvertibleMigration.new.migrate(:up)
|
|
horse1 = Horse.create
|
|
# populates existing horses with oldie = 1 but new ones have default 0
|
|
UpOnlyMigration.new.migrate(:up)
|
|
Horse.reset_column_information
|
|
horse1.reload
|
|
horse2 = Horse.create
|
|
|
|
assert 1, horse1.oldie # created before migration
|
|
assert 0, horse2.oldie # created after migration
|
|
|
|
UpOnlyMigration.new.migrate(:down) # should be no error
|
|
connection = ActiveRecord::Base.connection
|
|
assert_not connection.column_exists?(:horses, :oldie)
|
|
Horse.reset_column_information
|
|
end
|
|
end
|
|
end
|