require "cases/helper"
require 'bigdecimal/util'
require 'models/person'
require 'models/topic'
require MIGRATIONS_ROOT + "/valid/1_people_have_last_names"
require MIGRATIONS_ROOT + "/valid/2_we_need_reminders"
require MIGRATIONS_ROOT + "/decimal/1_give_me_big_numbers"
require MIGRATIONS_ROOT + "/interleaved/pass_3/2_i_raise_on_down"
if ActiveRecord::Base.connection.supports_migrations?
class BigNumber < ActiveRecord::Base; end
class Reminder < ActiveRecord::Base; end
class ActiveRecord::Migration
class <<self
attr_accessor :message_count
def puts(text="")
self.message_count ||= 0
self.message_count += 1
end
end
end
class MigrationTest < ActiveRecord::TestCase
self.use_transactional_fixtures = false
fixtures :people
def setup
ActiveRecord::Migration.verbose = true
PeopleHaveLastNames.message_count = 0
end
def teardown
ActiveRecord::Base.connection.initialize_schema_migrations_table
ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::Migrator.schema_migrations_table_name}"
%w(reminders people_reminders prefix_reminders_suffix).each do |table|
Reminder.connection.drop_table(table) rescue nil
end
Reminder.reset_column_information
%w(last_name key bio age height wealth birthday favorite_day
moment_of_truth male administrator funny).each do |column|
Person.connection.remove_column('people', column) rescue nil
end
Person.connection.remove_column("people", "first_name") rescue nil
Person.connection.remove_column("people", "middle_name") rescue nil
Person.connection.add_column("people", "first_name", :string, :limit => 40)
Person.reset_column_information
end
def test_add_index
# Limit size of last_name and key columns to support Firebird index limitations
Person.connection.add_column "people", "last_name", :string, :limit => 100
Person.connection.add_column "people", "key", :string, :limit => 100
Person.connection.add_column "people", "administrator", :boolean
assert_nothing_raised { Person.connection.add_index("people", "last_name") }
assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
# Orcl nds shrt indx nms. Sybs 2.
# OpenBase does not have named indexes. You must specify a single column name
unless current_adapter?(:OracleAdapter, :SybaseAdapter, :OpenBaseAdapter)
assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
assert_nothing_raised { Person.connection.remove_index("people", :column => ["last_name", "first_name"]) }
assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
assert_nothing_raised { Person.connection.remove_index("people", :name => "index_people_on_last_name_and_first_name") }
assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
assert_nothing_raised { Person.connection.remove_index("people", "last_name_and_first_name") }
assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
end
# quoting
# Note: changed index name from "key" to "key_idx" since "key" is a Firebird reserved word
# OpenBase does not have named indexes. You must specify a single column name
unless current_adapter?(:OpenBaseAdapter)
Person.update_all "#{Person.connection.quote_column_name 'key'}=#{Person.connection.quote_column_name 'id'}" #some databases (including sqlite2 won't add a unique index if existing data non unique)
assert_nothing_raised { Person.connection.add_index("people", ["key"], :name => "key_idx", :unique => true) }
assert_nothing_raised { Person.connection.remove_index("people", :name => "key_idx", :unique => true) }
end
# Sybase adapter does not support indexes on :boolean columns
# OpenBase does not have named indexes. You must specify a single column
unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
assert_nothing_raised { Person.connection.add_index("people", %w(last_name first_name administrator), :name => "named_admin") }
assert_nothing_raised { Person.connection.remove_index("people", :name => "named_admin") }
end
end
def test_create_table_adds_id
Person.connection.create_table :testings do |t|
t.column :foo, :string
end
assert_equal %w(foo id),
Person.connection.columns(:testings).map { |c| c.name }.sort
ensure
Person.connection.drop_table :testings rescue nil
end
def test_create_table_with_not_null_column
assert_nothing_raised do
Person.connection.create_table :testings do |t|
t.column :foo, :string, :null => false
end
end
assert_raises(ActiveRecord::StatementInvalid) do
Person.connection.execute "insert into testings (foo) values (NULL)"
end
ensure
Person.connection.drop_table :testings rescue nil
end
def test_create_table_with_defaults
# MySQL doesn't allow defaults on TEXT or BLOB columns.
mysql = current_adapter?(:MysqlAdapter)
Person.connection.create_table :testings do |t|
t.column :one, :string, :default => "hello"
t.column :two, :boolean, :default => true
t.column :three, :boolean, :default => false
t.column :four, :integer, :default => 1
t.column :five, :text, :default => "hello" unless mysql
end
columns = Person.connection.columns(:testings)
one = columns.detect { |c| c.name == "one" }
two = columns.detect { |c| c.name == "two" }
three = columns.detect { |c| c.name == "three" }
four = columns.detect { |c| c.name == "four" }
five = columns.detect { |c| c.name == "five" } unless mysql
assert_equal "hello", one.default
assert_equal true, two.default
assert_equal false, three.default
assert_equal 1, four.default
assert_equal "hello", five.default unless mysql
ensure
Person.connection.drop_table :testings rescue nil
end
def test_create_table_with_limits
assert_nothing_raised do
Person.connection.create_table :testings do |t|
t.column :foo, :string, :limit => 255
t.column :default_int, :integer
t.column :one_int, :integer, :limit => 1
t.column :four_int, :integer, :limit => 4
t.column :eight_int, :integer, :limit => 8
end
end
columns = Person.connection.columns(:testings)
foo = columns.detect { |c| c.name == "foo" }
assert_equal 255, foo.limit
default = columns.detect { |c| c.name == "default_int" }
one = columns.detect { |c| c.name == "one_int" }
four = columns.detect { |c| c.name == "four_int" }
eight = columns.detect { |c| c.name == "eight_int" }
if current_adapter?(:PostgreSQLAdapter)
assert_equal 'integer', default.sql_type
assert_equal 'smallint', one.sql_type
assert_equal 'integer', four.sql_type
assert_equal 'bigint', eight.sql_type
elsif current_adapter?(:OracleAdapter)
assert_equal 'NUMBER(38)', default.sql_type
assert_equal 'NUMBER(1)', one.sql_type
assert_equal 'NUMBER(4)', four.sql_type
assert_equal 'NUMBER(8)', eight.sql_type
end
ensure
Person.connection.drop_table :testings rescue nil
end
def test_create_table_with_primary_key_prefix_as_table_name_with_underscore
ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore
Person.connection.create_table :testings do |t|
t.column :foo, :string
end
assert_equal %w(foo testings_id), Person.connection.columns(:testings).map { |c| c.name }.sort
ensure
Person.connection.drop_table :testings rescue nil
ActiveRecord::Base.primary_key_prefix_type = nil
end
def test_create_table_with_primary_key_prefix_as_table_name
ActiveRecord::Base.primary_key_prefix_type = :table_name
Person.connection.create_table :testings do |t|
t.column :foo, :string
end
assert_equal %w(foo testingsid), Person.connection.columns(:testings).map { |c| c.name }.sort
ensure
Person.connection.drop_table :testings rescue nil
ActiveRecord::Base.primary_key_prefix_type = nil
end
# SQL Server, Sybase, and SQLite3 will not allow you to add a NOT NULL
# column to a table without a default value.
unless current_adapter?(:SQLServerAdapter, :SybaseAdapter, :SQLiteAdapter)
def test_add_column_not_null_without_default
Person.connection.create_table :testings do |t|
t.column :foo, :string
end
Person.connection.add_column :testings, :bar, :string, :null => false
assert_raises(ActiveRecord::StatementInvalid) do
Person.connection.execute "insert into testings (foo, bar) values ('hello', NULL)"
end
ensure
Person.connection.drop_table :testings rescue nil
end
end
def test_add_column_not_null_with_default
Person.connection.create_table :testings do |t|
t.column :foo, :string
end
con = Person.connection
Person.connection.enable_identity_insert("testings", true) if current_adapter?(:SybaseAdapter)
Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}) values (1, 'hello')"
Person.connection.enable_identity_insert("testings", false) if current_adapter?(:SybaseAdapter)
assert_nothing_raised {Person.connection.add_column :testings, :bar, :string, :null => false, :default => "default" }
assert_raises(ActiveRecord::StatementInvalid) do
unless current_adapter?(:OpenBaseAdapter)
Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) values (2, 'hello', NULL)"
else
Person.connection.insert("INSERT INTO testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) VALUES (2, 'hello', NULL)",
"Testing Insert","id",2)
end
end
ensure
Person.connection.drop_table :testings rescue nil
end
# We specifically do a manual INSERT here, and then test only the SELECT
# functionality. This allows us to more easily catch INSERT being broken,
# but SELECT actually working fine.
def test_native_decimal_insert_manual_vs_automatic
correct_value = '0012345678901234567890.0123456789'.to_d
Person.delete_all
Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
Person.reset_column_information
# Do a manual insertion
if current_adapter?(:OracleAdapter)
Person.connection.execute "insert into people (id, wealth) values (people_seq.nextval, 12345678901234567890.0123456789)"
elsif current_adapter?(:OpenBaseAdapter)
Person.connection.execute "insert into people (wealth) values ('12345678901234567890.0123456789')"
else
Person.connection.execute "insert into people (wealth) values (12345678901234567890.0123456789)"
end
# SELECT
row = Person.find(:first)
assert_kind_of BigDecimal, row.wealth
# If this assert fails, that means the SELECT is broken!
unless current_adapter?(:SQLite3Adapter)
assert_equal correct_value, row.wealth
end
# Reset to old state
Person.delete_all
# Now use the Rails insertion
assert_nothing_raised { Person.create :wealth => BigDecimal.new("12345678901234567890.0123456789") }
# SELECT
row = Person.find(:first)
assert_kind_of BigDecimal, row.wealth
# If these asserts fail, that means the INSERT (create function, or cast to SQL) is broken!
unless current_adapter?(:SQLite3Adapter)
assert_equal correct_value, row.wealth
end
# Reset to old state
Person.connection.del_column "people", "wealth" rescue nil
Person.reset_column_information
end
def test_add_column_with_precision_and_scale
Person.connection.add_column 'people', 'wealth', :decimal, :precision => 9, :scale => 7
Person.reset_column_information
wealth_column = Person.columns_hash['wealth']
assert_equal 9, wealth_column.precision
assert_equal 7, wealth_column.scale
end
def test_native_types
Person.delete_all
Person.connection.add_column "people", "last_name", :string
Person.connection.add_column "people", "bio", :text
Person.connection.add_column "people", "age", :integer
Person