mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Fix type casting column default in change_column
Since #31230, `change_column` is executed as a bulk statement. That caused incorrect type casting column default by looking up the before changed type, not the after changed type. In a bulk statement, we can't use `change_column_default_for_alter` if the statement changes the column type. This fixes the type casting to use the constructed target sql_type. Fixes #34938.
This commit is contained in:
parent
dd8d37881c
commit
4a0e3809be
4 changed files with 56 additions and 28 deletions
|
@ -17,6 +17,42 @@ module ActiveRecord
|
|||
"VALIDATE CONSTRAINT #{quote_column_name(name)}"
|
||||
end
|
||||
|
||||
def visit_ChangeColumnDefinition(o)
|
||||
column = o.column
|
||||
column.sql_type = type_to_sql(column.type, column.options)
|
||||
quoted_column_name = quote_column_name(o.name)
|
||||
|
||||
change_column_sql = +"ALTER COLUMN #{quoted_column_name} TYPE #{column.sql_type}"
|
||||
|
||||
options = column_options(column)
|
||||
|
||||
if options[:collation]
|
||||
change_column_sql << " COLLATE \"#{options[:collation]}\""
|
||||
end
|
||||
|
||||
if options[:using]
|
||||
change_column_sql << " USING #{options[:using]}"
|
||||
elsif options[:cast_as]
|
||||
cast_as_type = type_to_sql(options[:cast_as], options)
|
||||
change_column_sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})"
|
||||
end
|
||||
|
||||
if options.key?(:default)
|
||||
if options[:default].nil?
|
||||
change_column_sql << ", ALTER COLUMN #{quoted_column_name} DROP DEFAULT"
|
||||
else
|
||||
quoted_default = quote_default_expression(options[:default], column)
|
||||
change_column_sql << ", ALTER COLUMN #{quoted_column_name} SET DEFAULT #{quoted_default}"
|
||||
end
|
||||
end
|
||||
|
||||
if options.key?(:null)
|
||||
change_column_sql << ", ALTER COLUMN #{quoted_column_name} #{options[:null] ? 'DROP' : 'SET'} NOT NULL"
|
||||
end
|
||||
|
||||
change_column_sql
|
||||
end
|
||||
|
||||
def add_column_options!(sql, options)
|
||||
if options[:collation]
|
||||
sql << " COLLATE \"#{options[:collation]}\""
|
||||
|
|
|
@ -683,38 +683,20 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
def change_column_sql(table_name, column_name, type, options = {})
|
||||
quoted_column_name = quote_column_name(column_name)
|
||||
sql_type = type_to_sql(type, options)
|
||||
sql = +"ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}"
|
||||
if options[:collation]
|
||||
sql << " COLLATE \"#{options[:collation]}\""
|
||||
end
|
||||
if options[:using]
|
||||
sql << " USING #{options[:using]}"
|
||||
elsif options[:cast_as]
|
||||
cast_as_type = type_to_sql(options[:cast_as], options)
|
||||
sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})"
|
||||
end
|
||||
|
||||
sql
|
||||
end
|
||||
|
||||
def add_column_for_alter(table_name, column_name, type, options = {})
|
||||
return super unless options.key?(:comment)
|
||||
[super, Proc.new { change_column_comment(table_name, column_name, options[:comment]) }]
|
||||
end
|
||||
|
||||
def change_column_for_alter(table_name, column_name, type, options = {})
|
||||
sqls = [change_column_sql(table_name, column_name, type, options)]
|
||||
sqls << change_column_default_for_alter(table_name, column_name, options[:default]) if options.key?(:default)
|
||||
sqls << change_column_null_for_alter(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
|
||||
td = create_table_definition(table_name)
|
||||
cd = td.new_column_definition(column_name, type, options)
|
||||
sqls = [schema_creation.accept(ChangeColumnDefinition.new(cd, column_name))]
|
||||
sqls << Proc.new { change_column_comment(table_name, column_name, options[:comment]) } if options.key?(:comment)
|
||||
sqls
|
||||
end
|
||||
|
||||
# Changes the default value of a table column.
|
||||
def change_column_default_for_alter(table_name, column_name, default_or_changes) # :nodoc:
|
||||
def change_column_default_for_alter(table_name, column_name, default_or_changes)
|
||||
column = column_for(table_name, column_name)
|
||||
return unless column
|
||||
|
||||
|
@ -729,8 +711,8 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
def change_column_null_for_alter(table_name, column_name, null, default = nil) #:nodoc:
|
||||
"ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
|
||||
def change_column_null_for_alter(table_name, column_name, null, default = nil)
|
||||
"ALTER COLUMN #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
|
||||
end
|
||||
|
||||
def add_timestamps_for_alter(table_name, options = {})
|
||||
|
|
|
@ -36,9 +36,7 @@ module ActiveRecord
|
|||
class V5_1 < V5_2
|
||||
def change_column(table_name, column_name, type, options = {})
|
||||
if adapter_name == "PostgreSQL"
|
||||
clear_cache!
|
||||
sql = connection.send(:change_column_sql, table_name, column_name, type, options)
|
||||
execute "ALTER TABLE #{quote_table_name(table_name)} #{sql}"
|
||||
super(table_name, column_name, type, options.except(:default, :null, :comment))
|
||||
change_column_default(table_name, column_name, options[:default]) if options.key?(:default)
|
||||
change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
|
||||
change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
|
||||
|
|
|
@ -17,7 +17,7 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
|
|||
enable_extension!("hstore", @connection)
|
||||
|
||||
@connection.transaction do
|
||||
@connection.create_table("pg_arrays") do |t|
|
||||
@connection.create_table "pg_arrays", force: true do |t|
|
||||
t.string "tags", array: true, limit: 255
|
||||
t.integer "ratings", array: true
|
||||
t.datetime :datetimes, array: true
|
||||
|
@ -112,6 +112,18 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
|
|||
assert_predicate column, :array?
|
||||
end
|
||||
|
||||
def test_change_column_from_non_array_to_array
|
||||
@connection.add_column :pg_arrays, :snippets, :string
|
||||
@connection.change_column :pg_arrays, :snippets, :text, array: true, default: [], using: "string_to_array(\"snippets\", ',')"
|
||||
|
||||
PgArray.reset_column_information
|
||||
column = PgArray.columns_hash["snippets"]
|
||||
|
||||
assert_equal :text, column.type
|
||||
assert_equal [], PgArray.column_defaults["snippets"]
|
||||
assert_predicate column, :array?
|
||||
end
|
||||
|
||||
def test_change_column_cant_make_non_array_column_to_array
|
||||
@connection.add_column :pg_arrays, :a_string, :string
|
||||
assert_raises ActiveRecord::StatementInvalid do
|
||||
|
|
Loading…
Reference in a new issue