Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

#### Changed

- [#1381](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1381) Fix `change_column` to preserve old column attributes.

#### Fixed

Please check [8-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/8-1-stable/CHANGELOG.md) for previous changes.
Original file line number Diff line number Diff line change
Expand Up @@ -196,14 +196,16 @@ def change_column(table_name, column_name, type, options = {})
end

column_object = schema_cache.columns(table_name).find { |c| c.name.to_s == column_name.to_s }
without_constraints = options.key?(:default) || options.key?(:limit)
changing_type = column_object && column_object.type != type.to_sym
no_constraint_options = options.key?(:default) || options.key?(:limit)

default = if !options.key?(:default) && column_object
column_object.default
else
options[:default]
end

if without_constraints || (column_object && column_object.type != type.to_sym)
if no_constraint_options || changing_type
remove_default_constraint(table_name, column_name)
indexes = indexes(table_name).select { |index| index.columns.include?(column_name.to_s) }
remove_indexes(table_name, column_name)
Expand All @@ -212,10 +214,14 @@ def change_column(table_name, column_name, type, options = {})
sql_commands << "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(options[:default], column_object)} WHERE #{quote_column_name(column_name)} IS NULL" if !options[:null].nil? && options[:null] == false && !options[:default].nil?
alter_command = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, limit: options[:limit], precision: options[:precision], scale: options[:scale])}"
alter_command += " COLLATE #{options[:collation]}" if options[:collation].present?
alter_command += " NOT NULL" if !options[:null].nil? && options[:null] == false
if !options[:null].nil?
alter_command += " NOT NULL" if options[:null] == false
elsif column_object && !column_object.null
alter_command += " NOT NULL"
end
sql_commands << alter_command

if without_constraints
if no_constraint_options || (changing_type && default.present?)
default = quote_default_expression(default, column_object || column_for(table_name, column_name))
sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{default} FOR #{quote_column_name(column_name)}"
Comment on lines +224 to 226
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default.present? will evaluate to false for valid defaults like false and "", so changing a column’s type would still drop those defaults. Consider checking for nil instead (and also preserving column_object.default_function when present) so boolean/empty-string/function defaults are retained across type changes.

Copilot uses AI. Check for mistakes.
end
Expand Down
4 changes: 3 additions & 1 deletion test/cases/migration_test_sqlserver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,13 @@ class MigrationTestSQLServer < ActiveRecord::TestCase
lock_version_column = Person.columns_hash["lock_version"]
assert_equal :integer, lock_version_column.type
assert lock_version_column.default.present?
assert_equal 0, lock_version_column.default
assert_nothing_raised { connection.change_column "people", "lock_version", :string }
Person.reset_column_information
lock_version_column = Person.columns_hash["lock_version"]
assert_equal :string, lock_version_column.type
assert lock_version_column.default.nil?
assert lock_version_column.default.present?
assert_equal "0", lock_version_column.default
assert_nothing_raised { connection.change_column "people", "lock_version", :integer }
Person.reset_column_information
end
Expand Down
Loading