Skip to content

Commit

Permalink
Fixed mysql change_column_default to not make the column always nulla…
Browse files Browse the repository at this point in the history
…ble.

Also added change_column_null to both mysql and sqlite to keep the api features closer to postgresql.

[#617 state:resolved]
  • Loading branch information
tarmo authored and jeremy committed Jul 14, 2008
1 parent 8f72bc9 commit 07578ac
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 7 deletions.
2 changes: 2 additions & 0 deletions activerecord/CHANGELOG
@@ -1,5 +1,7 @@
*Edge* *Edge*


* change_column_default preserves the not-null constraint. #617 [Tarmo Tänav]

* Fixed that create database statements would always include "DEFAULT NULL" (Nick Sieger) [#334] * Fixed that create database statements would always include "DEFAULT NULL" (Nick Sieger) [#334]


* Add :accessible option to associations for allowing (opt-in) mass assignment. #474. [David Dollar] Example : * Add :accessible option to associations for allowing (opt-in) mass assignment. #474. [David Dollar] Example :
Expand Down
Expand Up @@ -437,18 +437,29 @@ def rename_table(table_name, new_name)
end end


def change_column_default(table_name, column_name, default) #:nodoc: def change_column_default(table_name, column_name, default) #:nodoc:
current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"] column = column_for(table_name, column_name)
change_column table_name, column_name, column.sql_type, :default => default
end

def change_column_null(table_name, column_name, null, default = nil)
column = column_for(table_name, column_name)

unless null || default.nil?
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
end


execute("ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{current_type} DEFAULT #{quote(default)}") change_column table_name, column_name, column.sql_type, :null => null
end end


def change_column(table_name, column_name, type, options = {}) #:nodoc: def change_column(table_name, column_name, type, options = {}) #:nodoc:
column = column_for(table_name, column_name)

unless options_include_default?(options) unless options_include_default?(options)
if column = columns(table_name).find { |c| c.name == column_name.to_s } options[:default] = column.default
options[:default] = column.default end
else
raise "No such column: #{table_name}.#{column_name}" unless options.has_key?(:null)
end options[:null] = column.null
end end


change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
Expand All @@ -460,6 +471,7 @@ def rename_column(table_name, column_name, new_column_name) #:nodoc:
options = {} options = {}
if column = columns(table_name).find { |c| c.name == column_name.to_s } if column = columns(table_name).find { |c| c.name == column_name.to_s }
options[:default] = column.default options[:default] = column.default
options[:null] = column.null
else else
raise ActiveRecordError, "No such column: #{table_name}.#{column_name}" raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
end end
Expand Down Expand Up @@ -536,6 +548,13 @@ def supports_views?
def version def version
@version ||= @connection.server_info.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i } @version ||= @connection.server_info.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
end end

def column_for(table_name, column_name)
unless column = columns(table_name).find { |c| c.name == column_name.to_s }
raise "No such column: #{table_name}.#{column_name}"
end
column
end
end end
end end
end end
Expand Up @@ -238,6 +238,15 @@ def change_column_default(table_name, column_name, default) #:nodoc:
end end
end end


def change_column_null(table_name, column_name, null, default = nil)
unless null || default.nil?
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
end
alter_table(table_name) do |definition|
definition[column_name].null = null
end
end

def change_column(table_name, column_name, type, options = {}) #:nodoc: def change_column(table_name, column_name, type, options = {}) #:nodoc:
alter_table(table_name) do |definition| alter_table(table_name) do |definition|
include_default = options_include_default?(options) include_default = options_include_default?(options)
Expand Down
49 changes: 49 additions & 0 deletions activerecord/test/cases/migration_test.rb
Expand Up @@ -703,6 +703,55 @@ def test_change_column_quotes_column_names
Person.connection.drop_table :testings rescue nil Person.connection.drop_table :testings rescue nil
end end


def test_keeping_default_and_notnull_constaint_on_change
Person.connection.create_table :testings do |t|
t.column :title, :string
end
person_klass = Class.new(Person)
person_klass.set_table_name 'testings'

person_klass.connection.add_column "testings", "wealth", :integer, :null => false, :default => 99
person_klass.reset_column_information
assert_equal 99, person_klass.columns_hash["wealth"].default
assert_equal false, person_klass.columns_hash["wealth"].null
assert_nothing_raised {person_klass.connection.execute("insert into testings (title) values ('tester')")}

# change column default to see that column doesn't lose its not null definition
person_klass.connection.change_column_default "testings", "wealth", 100
person_klass.reset_column_information
assert_equal 100, person_klass.columns_hash["wealth"].default
assert_equal false, person_klass.columns_hash["wealth"].null

# rename column to see that column doesn't lose its not null and/or default definition
person_klass.connection.rename_column "testings", "wealth", "money"
person_klass.reset_column_information
assert_nil person_klass.columns_hash["wealth"]
assert_equal 100, person_klass.columns_hash["money"].default
assert_equal false, person_klass.columns_hash["money"].null

# change column
person_klass.connection.change_column "testings", "money", :integer, :null => false, :default => 1000
person_klass.reset_column_information
assert_equal 1000, person_klass.columns_hash["money"].default
assert_equal false, person_klass.columns_hash["money"].null

# change column, make it nullable and clear default
person_klass.connection.change_column "testings", "money", :integer, :null => true, :default => nil
person_klass.reset_column_information
assert_nil person_klass.columns_hash["money"].default
assert_equal true, person_klass.columns_hash["money"].null

# change_column_null, make it not nullable and set null values to a default value
person_klass.connection.execute('UPDATE testings SET money = NULL')
person_klass.connection.change_column_null "testings", "money", false, 2000
person_klass.reset_column_information
assert_nil person_klass.columns_hash["money"].default
assert_equal false, person_klass.columns_hash["money"].null
assert_equal [2000], Person.connection.select_values("SELECT money FROM testings").map { |s| s.to_i }.sort
ensure
Person.connection.drop_table :testings rescue nil
end

def test_change_column_default_to_null def test_change_column_default_to_null
Person.connection.change_column_default "people", "first_name", nil Person.connection.change_column_default "people", "first_name", nil
Person.reset_column_information Person.reset_column_information
Expand Down

0 comments on commit 07578ac

Please sign in to comment.