Skip to content

Commit b9b8852

Browse files
Use SET DEFAULT when changing a column default in MySQL
Aligns the SQL produced for changing a column default between MySQL and PostgreSQL. MySQL is currently producing an <ALTER TABLE x CHANGE COLUMN y> query just to change the default for a column, which is less efficient than using the SET DEFAULT syntax. Additionally, extracts ChangeColumnDefaultDefinition and moves the SQL generation to the visitor class.
1 parent 6d100e5 commit b9b8852

File tree

7 files changed

+32
-17
lines changed

7 files changed

+32
-17
lines changed

activerecord/lib/active_record/connection_adapters.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module ConnectionAdapters
1818
autoload :IndexDefinition
1919
autoload :ColumnDefinition
2020
autoload :ChangeColumnDefinition
21+
autoload :ChangeColumnDefaultDefinition
2122
autoload :ForeignKeyDefinition
2223
autoload :CheckConstraintDefinition
2324
autoload :TableDefinition

activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ def aliased_types(name, fallback)
9494

9595
ChangeColumnDefinition = Struct.new(:column, :name) # :nodoc:
9696

97+
ChangeColumnDefaultDefinition = Struct.new(:column, :default, :ddl) # :nodoc:
98+
9799
CreateIndexDefinition = Struct.new(:index, :algorithm, :if_not_exists, :ddl) # :nodoc:
98100

99101
PrimaryKeyDefinition = Struct.new(:name) # :nodoc:

activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1674,6 +1674,14 @@ def add_column_for_alter(table_name, column_name, type, **options)
16741674
schema_creation.accept(AddColumnDefinition.new(cd))
16751675
end
16761676

1677+
def change_column_default_for_alter(table_name, column_name, default_or_changes)
1678+
column = column_for(table_name, column_name)
1679+
return unless column
1680+
1681+
default = extract_new_default_value(default_or_changes)
1682+
schema_creation.accept(ChangeColumnDefaultDefinition.new(column, default))
1683+
end
1684+
16771685
def rename_column_sql(table_name, column_name, new_column_name)
16781686
"RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
16791687
end

activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -348,8 +348,7 @@ def rename_index(table_name, old_name, new_name)
348348
end
349349

350350
def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
351-
default = extract_new_default_value(default_or_changes)
352-
change_column table_name, column_name, nil, default: default
351+
execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_default_for_alter(table_name, column_name, default_or_changes)}"
353352
end
354353

355354
def change_column_null(table_name, column_name, null, default = nil) # :nodoc:

activerecord/lib/active_record/connection_adapters/mysql/schema_creation.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ def visit_ChangeColumnDefinition(o)
2424
add_column_position!(change_column_sql, column_options(o.column))
2525
end
2626

27+
def visit_ChangeColumnDefaultDefinition(o)
28+
sql = +"ALTER COLUMN #{quote_column_name(o.column.name)} SET DEFAULT "
29+
if o.default.nil?
30+
sql << "NULL"
31+
else
32+
sql << quote_default_expression(o.default, o.column)
33+
end
34+
o.ddl = sql
35+
end
36+
2737
def visit_CreateIndexDefinition(o)
2838
sql = visit_IndexDefinition(o.index, true)
2939
sql << " #{o.algorithm}" if o.algorithm

activerecord/lib/active_record/connection_adapters/postgresql/schema_creation.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,16 @@ def visit_ChangeColumnDefinition(o)
8787
change_column_sql
8888
end
8989

90+
def visit_ChangeColumnDefaultDefinition(o)
91+
sql = +"ALTER COLUMN #{quote_column_name(o.column.name)} "
92+
if o.default.nil?
93+
sql << "DROP DEFAULT"
94+
else
95+
sql << "SET DEFAULT #{quote_default_expression(o.default, o.column)}"
96+
end
97+
o.ddl = sql
98+
end
99+
90100
def add_column_options!(sql, options)
91101
if options[:collation]
92102
sql << " COLLATE \"#{options[:collation]}\""

activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -842,21 +842,6 @@ def change_column_for_alter(table_name, column_name, type, **options)
842842
sqls
843843
end
844844

845-
def change_column_default_for_alter(table_name, column_name, default_or_changes)
846-
column = column_for(table_name, column_name)
847-
return unless column
848-
849-
default = extract_new_default_value(default_or_changes)
850-
alter_column_query = "ALTER COLUMN #{quote_column_name(column_name)} %s"
851-
if default.nil?
852-
# <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
853-
# cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
854-
alter_column_query % "DROP DEFAULT"
855-
else
856-
alter_column_query % "SET DEFAULT #{quote_default_expression(default, column)}"
857-
end
858-
end
859-
860845
def change_column_null_for_alter(table_name, column_name, null, default = nil)
861846
if default.nil?
862847
"ALTER COLUMN #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"

0 commit comments

Comments
 (0)