Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make change_column_comment and change_table_comment invertible #35970

Merged
merged 1 commit into from
Apr 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions activerecord/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
* `change_column_comment` and `change_table_comment` are invertible only if
`to` and `from` options are specified.

*Yoshiyuki Kinjo*

* Don't call commit/rollback callbacks despite a record isn't saved.

Fixes #29747.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1185,12 +1185,22 @@ def options_include_default?(options)
end

# Changes the comment for a table or removes it if +nil+.
def change_table_comment(table_name, comment)
#
# Passing a hash containing +:from+ and +:to+ will make this change
# reversible in migration:
#
# change_table_comment(:posts, from: "old_comment", to: "new_comment")
def change_table_comment(table_name, comment_or_changes)
raise NotImplementedError, "#{self.class} does not support changing table comments"
end

# Changes the comment for a column or removes it if +nil+.
def change_column_comment(table_name, column_name, comment)
#
# Passing a hash containing +:from+ and +:to+ will make this change
# reversible in migration:
#
# change_column_comment(:posts, :state, from: "old_comment", to: "new_comment")
def change_column_comment(table_name, column_name, comment_or_changes)
raise NotImplementedError, "#{self.class} does not support changing column comments"
end

Expand Down Expand Up @@ -1374,6 +1384,7 @@ def extract_new_default_value(default_or_changes)
default_or_changes
end
end
alias :extract_new_comment_value :extract_new_default_value

def can_remove_index_by_name?(options)
options.is_a?(Hash) && options.key?(:name) && options.except(:name, :algorithm).empty?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,8 @@ def table_comment(table_name) # :nodoc:
SQL
end

def change_table_comment(table_name, comment) #:nodoc:
def change_table_comment(table_name, comment_or_changes) # :nodoc:
comment = extract_new_comment_value(comment_or_changes)
comment = "" if comment.nil?
execute("ALTER TABLE #{quote_table_name(table_name)} COMMENT #{quote(comment)}")
end
Expand Down Expand Up @@ -341,7 +342,8 @@ def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
change_column table_name, column_name, nil, null: null
end

def change_column_comment(table_name, column_name, comment) #:nodoc:
def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
comment = extract_new_comment_value(comment_or_changes)
change_column table_name, column_name, nil, comment: comment
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -418,14 +418,16 @@ def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
end

# Adds comment for given table column or drops it if +comment+ is a +nil+
def change_column_comment(table_name, column_name, comment) # :nodoc:
def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
clear_cache!
comment = extract_new_comment_value(comment_or_changes)
execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} IS #{quote(comment)}"
end

# Adds comment for given table or drops it if +comment+ is a +nil+
def change_table_comment(table_name, comment) # :nodoc:
def change_table_comment(table_name, comment_or_changes) # :nodoc:
clear_cache!
comment = extract_new_comment_value(comment_or_changes)
execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS #{quote(comment)}"
end

Expand Down
25 changes: 24 additions & 1 deletion activerecord/lib/active_record/migration/command_recorder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ class Migration
# * change_column
# * change_column_default (must supply a :from and :to option)
# * change_column_null
# * change_column_comment (must supply a :from and :to option)
# * change_table_comment (must supply a :from and :to option)
# * create_join_table
# * create_table
# * disable_extension
Expand All @@ -35,7 +37,8 @@ class CommandRecorder
:change_column_default, :add_reference, :remove_reference, :transaction,
:drop_join_table, :drop_table, :execute_block, :enable_extension, :disable_extension,
:change_column, :execute, :remove_columns, :change_column_null,
:add_foreign_key, :remove_foreign_key
:add_foreign_key, :remove_foreign_key,
:change_column_comment, :change_table_comment
]
include JoinTable

Expand Down Expand Up @@ -244,6 +247,26 @@ def invert_remove_foreign_key(args)
[:add_foreign_key, reversed_args]
end

def invert_change_column_comment(args)
table, column, options = *args

unless options && options.is_a?(Hash) && options.has_key?(:from) && options.has_key?(:to)
raise ActiveRecord::IrreversibleMigration, "change_column_comment is only reversible if given a :from and :to option."
end

[:change_column_comment, [table, column, from: options[:to], to: options[:from]]]
end

def invert_change_table_comment(args)
table, options = *args

unless options && options.is_a?(Hash) && options.has_key?(:from) && options.has_key?(:to)
raise ActiveRecord::IrreversibleMigration, "change_table_comment is only reversible if given a :from and :to option."
end

[:change_table_comment, [table, from: options[:to], to: options[:from]]]
end

def respond_to_missing?(method, _)
super || delegate.respond_to?(method)
end
Expand Down
10 changes: 10 additions & 0 deletions activerecord/lib/active_record/migration/compatibility.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ module CommandRecorder
def invert_transaction(args, &block)
[:transaction, args, block]
end

def invert_change_column_comment(args)
table_name, column_name, comment = args
[:change_column_comment, [table_name, column_name, from: comment, to: comment]]
end

def invert_change_table_comment(args)
table_name, comment = args
[:change_table_comment, [table_name, from: comment, to: comment]]
end
end

def create_table(table_name, **options)
Expand Down
59 changes: 59 additions & 0 deletions activerecord/test/cases/invertible_migration_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,32 @@ def change
end
end

class ChangeColumnComment1 < SilentMigration
def change
create_table("horses") do |t|
t.column :name, :string, comment: "Sekitoba"
end
end
end

class ChangeColumnComment2 < SilentMigration
def change
change_column_comment :horses, :name, from: "Sekitoba", to: "Diomed"
end
end

class ChangeTableComment1 < SilentMigration
def change
create_table("horses", comment: "Sekitoba")
end
end

class ChangeTableComment2 < SilentMigration
def change
change_table_comment :horses, from: "Sekitoba", to: "Diomed"
end
end

class DisableExtension1 < SilentMigration
def change
enable_extension "hstore"
Expand Down Expand Up @@ -290,6 +316,7 @@ def test_migrate_revert_transaction
def test_migrate_revert_change_column_default
migration1 = ChangeColumnDefault1.new
migration1.migrate(:up)
Horse.reset_column_information
assert_equal "Sekitoba", Horse.new.name

migration2 = ChangeColumnDefault2.new
Expand All @@ -302,6 +329,38 @@ def test_migrate_revert_change_column_default
assert_equal "Sekitoba", Horse.new.name
end

if ActiveRecord::Base.connection.supports_comments?
def test_migrate_revert_change_column_comment
migration1 = ChangeColumnComment1.new
migration1.migrate(:up)
Horse.reset_column_information
assert_equal "Sekitoba", Horse.columns_hash["name"].comment

migration2 = ChangeColumnComment2.new
migration2.migrate(:up)
Horse.reset_column_information
assert_equal "Diomed", Horse.columns_hash["name"].comment

migration2.migrate(:down)
Horse.reset_column_information
assert_equal "Sekitoba", Horse.columns_hash["name"].comment
end

def test_migrate_revert_change_table_comment
connection = ActiveRecord::Base.connection
migration1 = ChangeTableComment1.new
migration1.migrate(:up)
assert_equal "Sekitoba", connection.table_comment("horses")

migration2 = ChangeTableComment2.new
migration2.migrate(:up)
assert_equal "Diomed", connection.table_comment("horses")

migration2.migrate(:down)
assert_equal "Sekitoba", connection.table_comment("horses")
end
end

if current_adapter?(:PostgreSQLAdapter)
def test_migrate_enable_and_disable_extension
migration1 = InvertibleMigration.new
Expand Down
34 changes: 34 additions & 0 deletions activerecord/test/cases/migration/command_recorder_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,40 @@ def test_invert_change_column_default_with_from_and_to_with_boolean
assert_equal [:change_column_default, [:table, :column, from: false, to: true]], change
end

if ActiveRecord::Base.connection.supports_comments?
def test_invert_change_column_comment
assert_raises(ActiveRecord::IrreversibleMigration) do
@recorder.inverse_of :change_column_comment, [:table, :column, "comment"]
end
end

def test_invert_change_column_comment_with_from_and_to
change = @recorder.inverse_of :change_column_comment, [:table, :column, from: "old_value", to: "new_value"]
assert_equal [:change_column_comment, [:table, :column, from: "new_value", to: "old_value"]], change
end

def test_invert_change_column_comment_with_from_and_to_with_nil
change = @recorder.inverse_of :change_column_comment, [:table, :column, from: nil, to: "new_value"]
assert_equal [:change_column_comment, [:table, :column, from: "new_value", to: nil]], change
end

def test_invert_change_table_comment
assert_raises(ActiveRecord::IrreversibleMigration) do
@recorder.inverse_of :change_column_comment, [:table, :column, "comment"]
end
end

def test_invert_change_table_comment_with_from_and_to
change = @recorder.inverse_of :change_table_comment, [:table, from: "old_value", to: "new_value"]
assert_equal [:change_table_comment, [:table, from: "new_value", to: "old_value"]], change
end

def test_invert_change_table_comment_with_from_and_to_with_nil
change = @recorder.inverse_of :change_table_comment, [:table, from: nil, to: "new_value"]
assert_equal [:change_table_comment, [:table, from: "new_value", to: nil]], change
end
end

def test_invert_change_column_null
add = @recorder.inverse_of :change_column_null, [:table, :column, true]
assert_equal [:change_column_null, [:table, :column, false]], add
Expand Down
29 changes: 29 additions & 0 deletions activerecord/test/cases/migration/compatibility_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,35 @@ def change
end
end

if ActiveRecord::Base.connection.supports_comments?
def test_change_column_comment_can_be_reverted
migration = Class.new(ActiveRecord::Migration[5.2]) {
def migrate(x)
revert do
change_column_comment(:testings, :foo, "comment")
end
end
}.new

ActiveRecord::Migrator.new(:up, [migration]).migrate
assert connection.column_exists?(:testings, :foo, comment: "comment")
end

def test_change_table_comment_can_be_reverted
migration = Class.new(ActiveRecord::Migration[5.2]) {
def migrate(x)
revert do
change_table_comment(:testings, "comment")
end
end
}.new

ActiveRecord::Migrator.new(:up, [migration]).migrate

assert_equal "comment", connection.table_comment("testings")
end
end

if current_adapter?(:PostgreSQLAdapter)
class Testing < ActiveRecord::Base
end
Expand Down