1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Added support for ODBC connections to MS SQL Server so you can connect from a non-Windows machine #1569 [Mark Imbriaco/DeLynn Berry] Added support for limit/offset with the MS SQL Server driver so that pagination will now work #1569 [DeLynn Berry]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@1583 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
David Heinemeier Hansson 2005-07-01 17:20:04 +00:00
parent 64612db7cf
commit f2a29ca43c
15 changed files with 234 additions and 130 deletions

View file

@ -1,5 +1,9 @@
*SVN*
* Added support for limit/offset with the MS SQL Server driver so that pagination will now work #1569 [DeLynn Berry]
* Added support for ODBC connections to MS SQL Server so you can connect from a non-Windows machine #1569 [Mark Imbriaco/DeLynn Berry]
* Fixed that multiparameter posts ignored attr_protected #1532 [alec+rails@veryclever.net]
* Fixed problem with eager loading when using a has_and_belongs_to_many association using :association_foreign_key #1504 [flash@vanklinkenbergsoftware.nl]

View file

@ -38,7 +38,7 @@ Rake::TestTask.new("test_mysql_ruby") { |t|
t.verbose = true
}
for adapter in %w( postgresql sqlite sqlite3 sqlserver db2 oci )
for adapter in %w( postgresql sqlite sqlite3 sqlserver sqlserver_odbc db2 oci )
Rake::TestTask.new("test_#{adapter}") { |t|
t.libs << "test" << "test/connections/native_#{adapter}"
t.pattern = "test/*_test{,_#{adapter}}.rb"

View file

@ -748,11 +748,7 @@ module ActiveRecord #:nodoc:
end
def add_limit!(sql, options)
if options[:limit] && options[:offset]
connection.add_limit_with_offset!(sql, options[:limit].to_i, options[:offset].to_i)
elsif options[:limit]
connection.add_limit_without_offset!(sql, options[:limit].to_i)
end
connection.add_limit_offset!(sql, options)
end
# Adds a sanitized version of +conditions+ to the +sql+ string. Note that it's the passed +sql+ string is changed.

View file

@ -352,21 +352,15 @@ module ActiveRecord
# Returns a string of the CREATE TABLE SQL statements for recreating the entire structure of the database.
def structure_dump() end
def add_limit!(sql, limit)
if limit.is_a? Array
limit, offset = *limit
add_limit_with_offset!(sql, limit.to_i, offset.to_i)
else
add_limit_without_offset!(sql, limit)
end
def add_limit!(sql, options)
return unless options
add_limit_offset!(sql, options)
end
def add_limit_with_offset!(sql, limit, offset)
sql << " LIMIT #{limit} OFFSET #{offset}"
end
def add_limit_without_offset!(sql, limit)
sql << " LIMIT #{limit}"
def add_limit_offset!(sql, options)
return if options[:limit].nil?
sql << " LIMIT #{options[:limit]}"
sql << " OFFSET #{options[:offset]}" if options.has_key?(:offset) and !options[:offset].nil?
end
def initialize_schema_information

View file

@ -172,11 +172,17 @@ module ActiveRecord
structure += select_one("SHOW CREATE TABLE #{table.to_a.first.last}")["Create Table"] + ";\n\n"
end
end
def add_limit_with_offset!(sql, limit, offset)
sql << " LIMIT #{offset}, #{limit}"
def add_limit_offset!(sql, options)
return if options[:limit].nil?
if options[:offset].blank?
sql << " LIMIT #{options[:limit]}"
else
sql << " LIMIT #{options[:offset]}, #{options[:limit]}"
end
end
def recreate_database(name)
drop_database(name)
create_database(name)

View file

@ -8,26 +8,49 @@ require 'active_record/connection_adapters/abstract_adapter'
# Modifications: DeLynn Berry <delynnb@megastarfinancial.com>
# Date: 3/22/2005
#
# This adapter will ONLY work on Windows systems, since it relies on Win32OLE, which,
# to my knowledge, is only available on Window.
# Modifications (ODBC): Mark Imbriaco <mark.imbriaco@pobox.com>
# Date: 6/26/2005
#
# It relies on the ADO support in the DBI module. If you are using the
# In ADO mode, this adapter will ONLY work on Windows systems,
# since it relies on Win32OLE, which, to my knowledge, is only
# available on Windows.
#
# This mode also relies on the ADO support in the DBI module. If you are using the
# one-click installer of Ruby, then you already have DBI installed, but
# the ADO module is *NOT* installed. You will need to get the latest
# source distribution of Ruby-DBI from http://ruby-dbi.sourceforge.net/
# unzip it, and copy the file <tt>src/lib/dbd_ado/ADO.rb</tt> to
# <tt>X:/Ruby/lib/ruby/site_ruby/1.8/DBD/ADO/ADO.rb</tt> (you will need to create
# the ADO directory). Once you've installed that file, you are ready to go.
# unzip it, and copy the file
# <tt>src/lib/dbd_ado/ADO.rb</tt>
# to
# <tt>X:/Ruby/lib/ruby/site_ruby/1.8/DBD/ADO/ADO.rb</tt>
# (you will more than likely need to create the ADO directory).
# Once you've installed that file, you are ready to go.
#
# In ODBC mode, the adapter requires the ODBC support in the DBI module which requires
# the Ruby ODBC module. Ruby ODBC 0.996 was used in development and testing,
# and it is available at http://www.ch-werner.de/rubyodbc/
#
# Options:
#
# * <tt>:host</tt> -- Defaults to localhost
# * <tt>:username</tt> -- Defaults to sa
# * <tt>:password</tt> -- Defaults to nothing
# * <tt>:database</tt> -- The name of the database. No default, must be provided.
# * <tt>:mode</tt> -- ADO or ODBC. Defaults to ADO.
# * <tt>:username</tt> -- Defaults to sa.
# * <tt>:password</tt> -- Defaults to empty string.
#
# I have tested this code on a WindowsXP Pro SP1 system,
# ruby 1.8.2 (2004-07-29) [i386-mswin32], SQL Server 2000.
# ADO specific options:
#
# * <tt>:host</tt> -- Defaults to localhost.
# * <tt>:database</tt> -- The name of the database. No default, must be provided.
#
# ODBC specific options:
#
# * <tt>:dsn</tt> -- Defaults to nothing.
#
# ADO code tested on Windows 2000 and higher systems,
# running ruby 1.8.2 (2004-07-29) [i386-mswin32], and SQL Server 2000 SP3.
#
# ODBC code tested on a Fedora Core 4 system, running FreeTDS 0.63,
# unixODBC 2.2.11, Ruby ODBC 0.996, Ruby DBI 0.0.23 and Ruby 1.8.2.
# [Linux strongmad 2.6.11-1.1369_FC4 #1 Thu Jun 2 22:55:56 EDT 2005 i686 i686 i386 GNU/Linux]
#
module ActiveRecord
class Base
@ -36,44 +59,47 @@ module ActiveRecord
symbolize_strings_in_hash(config)
host = config[:host]
username = config[:username] ? config[:username].to_s : 'sa'
password = config[:password].to_s
if config.has_key?(:database)
database = config[:database]
mode = config[:mode] ? config[:mode].to_s.upcase : 'ADO'
username = config[:username] ? config[:username].to_s : 'sa'
password = config[:password] ? config[:password].to_s : ''
if mode == "ODBC"
raise ArgumentError, "Missing DSN. Argument ':dsn' must be set in order for this adapter to work." unless config.has_key?(:dsn)
dsn = config[:dsn]
conn = DBI.connect("DBI:ODBC:#{dsn}", username, password)
else
raise ArgumentError, "No database specified. Missing argument: database."
raise ArgumentError, "Missing Database. Argument ':database' must be set in order for this adapter to work." unless config.has_key?(:database)
database = config[:database]
host = config[:host] ? config[:host].to_s : 'localhost'
conn = DBI.connect("DBI:ADO:Provider=SQLOLEDB;Data Source=#{host};Initial Catalog=#{database};User Id=#{username};Password=#{password};")
end
conn = DBI.connect("DBI:ADO:Provider=SQLOLEDB;Data Source=#{host};Initial Catalog=#{database};User Id=#{username};Password=#{password};")
conn["AutoCommit"] = true
ConnectionAdapters::SQLServerAdapter.new(conn, logger)
end
end
end # class Base
module ConnectionAdapters
class ColumnWithIdentity < Column# :nodoc:
attr_reader :identity, :scale
attr_reader :identity, :is_special, :scale
def initialize(name, default, sql_type = nil, is_identity = false, scale_value = 0)
super(name, default, sql_type)
@scale = scale_value
@identity = is_identity
@is_special = sql_type =~ /text|ntext|image/i ? true : false
@scale = scale_value
end
def simplified_type(field_type)
case field_type
when /int|bigint|smallint|tinyint/i : :integer
when /float|double|decimal|money|numeric|real|smallmoney/i : @scale == 0 ? :integer : :float
when /datetime|smalldatetime/i : :datetime
when /timestamp/i : :timestamp
when /time/i : :time
when /text|ntext/i : :text
when /binary|image|varbinary/i : :binary
when /char|nchar|nvarchar|string|varchar/i : :string
when /bit/i : :boolean
when /int|bigint|smallint|tinyint/i then :integer
when /float|double|decimal|money|numeric|real|smallmoney/i then @scale == 0 ? :integer : :float
when /datetime|smalldatetime/i then :datetime
when /timestamp/i then :timestamp
when /time/i then :time
when /text|ntext/i then :text
when /binary|image|varbinary/i then :binary
when /char|nchar|nvarchar|string|varchar/i then :string
when /bit/i then :boolean
end
end
@ -83,28 +109,32 @@ module ActiveRecord
when :string then value
when :integer then value == true || value == false ? value == true ? '1' : '0' : value.to_i
when :float then value.to_f
when :datetime then cast_to_date_or_time(value)
when :datetime then cast_to_datetime(value)
when :timestamp then cast_to_time(value)
when :time then cast_to_time(value)
else value
end
end
def cast_to_date_or_time(value)
return value if value.is_a?(Date)
guess_date_or_time (value.is_a?(Time)) ? value : cast_to_time(value)
end
def cast_to_time(value)
return value if value.is_a?(Time)
time_array = ParseDate.parsedate value
time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1;
Time.send Base.default_timezone, *time_array
time_array = ParseDate.parsedate(value)
time_array[0] ||= 2000
time_array[1] ||= 1
time_array[2] ||= 1
Time.send(Base.default_timezone, *time_array) rescue nil
end
def guess_date_or_time(value)
(value.hour == 0 and value.min == 0 and value.sec == 0) ?
Date.new(value.year, value.month, value.day) : value
def cast_to_datetime(value)
if value.is_a?(Time)
if value.year != 0 and value.month != 0 and value.day != 0
return value
else
return Time.mktime(2000, 1, 1, value.hour, value.min, value.sec) rescue nil
end
end
return cast_to_time(value) if value.is_a?(Date) or value.is_a?(String) rescue nil
value
end
# These methods will only allow the adapter to insert binary data with a length of 7K or less
@ -112,14 +142,10 @@ module ActiveRecord
def string_to_binary(value)
value.gsub(/(\r|\n|\0|\x1a)/) do
case $1
when "\r"
"%00"
when "\n"
"%01"
when "\0"
"%02"
when "\x1a"
"%03"
when "\r" then "%00"
when "\n" then "%01"
when "\0" then "%02"
when "\x1a" then "%03"
end
end
end
@ -127,22 +153,17 @@ module ActiveRecord
def binary_to_string(value)
value.gsub(/(%00|%01|%02|%03)/) do
case $1
when "%00"
"\r"
when "%01"
"\n"
when "%02\0"
"\0"
when "%03"
"\x1a"
when "%00" then "\r"
when "%01" then "\n"
when "%02\0" then "\0"
when "%03" then "\x1a"
end
end
end
end
end #class ColumnWithIdentity < Column
class SQLServerAdapter < AbstractAdapter
def native_database_types
{
:primary_key => "int NOT NULL IDENTITY(1, 1) PRIMARY KEY",
@ -164,7 +185,6 @@ module ActiveRecord
end
def select_all(sql, name = nil)
add_limit!(sql, nil)
select(sql, name)
end
@ -177,9 +197,9 @@ module ActiveRecord
def columns(table_name, name = nil)
sql = "SELECT COLUMN_NAME as ColName, COLUMN_DEFAULT as DefaultValue, DATA_TYPE as ColType, COL_LENGTH('#{table_name}', COLUMN_NAME) as Length, COLUMNPROPERTY(OBJECT_ID('#{table_name}'), COLUMN_NAME, 'IsIdentity') as IsIdentity, NUMERIC_SCALE as Scale FROM INFORMATION_SCHEMA.columns WHERE TABLE_NAME = '#{table_name}'"
result = nil
# Uncomment if you want to have the Columns select statment logged.
# Comment out if you want to have the Columns select statment logged.
# Personnally, I think it adds unneccessary bloat to the log.
# If you do uncomment, make sure to comment the "result" line that follows
# If you do comment it out, make sure to un-comment the "result" line that follows
log(sql, name, @connection) { |conn| result = conn.select_all(sql) }
#result = @connection.select_all(sql)
columns = []
@ -199,7 +219,7 @@ module ActiveRecord
execute enable_identity_insert(table_name, true)
ii_enabled = true
rescue Exception => e
# Coulnd't turn on IDENTITY_INSERT
raise ActiveRecordError, "IDENTITY_INSERT could not be turned ON"
end
end
end
@ -212,7 +232,7 @@ module ActiveRecord
begin
execute enable_identity_insert(table_name, false)
rescue Exception => e
# Couldn't turn off IDENTITY_INSERT
raise ActiveRecordError, "IDENTITY_INSERT could not be turned OFF"
end
end
end
@ -288,13 +308,13 @@ module ActiveRecord
"[#{name}]"
end
def add_limit_with_offset!(sql, limit, offset)
order_by = sql.include?("ORDER BY") ? get_order_by(sql.sub(/.*ORDER\sBY./, "")) : nil
sql.gsub!(/SELECT/i, "SELECT * FROM ( SELECT TOP #{limit} * FROM ( SELECT TOP #{limit + offset}")<<" ) AS tmp1 ORDER BY #{order_by[1]} ) AS tmp2 ORDER BY #{order_by[0]}"
end
def add_limit_without_offset!(sql, limit)
limit.nil? ? sql : sql.gsub!(/SELECT/i, "SELECT TOP #{limit}")
def add_limit_offset!(sql, options)
if options.has_key?(:limit) and options.has_key?(:offset) and !options[:limit].nil? and !options[:offset].nil?
options[:order] ||= "id ASC"
sql.gsub!(/SELECT/i, "SELECT * FROM ( SELECT TOP #{options[:limit]} * FROM ( SELECT TOP #{options[:limit] + options[:offset]}")<<" ) AS tmp1 ORDER BY #{change_order_direction(options[:order])} ) AS tmp2 ORDER BY #{options[:order]}"
else
sql.gsub!(/SELECT/i, "SELECT TOP #{options[:limit]}") unless options[:limit].nil?
end
end
def recreate_database(name)
@ -313,6 +333,7 @@ module ActiveRecord
private
def select(sql, name = nil)
rows = []
repair_special_columns(sql)
log(sql, name, @connection) do |conn|
conn.select_all(sql) do |row|
record = {}
@ -332,8 +353,9 @@ module ActiveRecord
end
def get_table_name(sql)
if sql =~ /into\s*([^\s]+)\s*/i or
sql =~ /update\s*([^\s]+)\s*/i
if sql =~ /into\s*([^\s]+)\s*|update\s*([^\s]+)\s*/i
$1
elsif sql =~ /from\s*([^\s]+)\s*/i
$1
else
nil
@ -345,14 +367,8 @@ module ActiveRecord
end
def get_identity_column(table_name)
if not @table_columns
@table_columns = {}
end
if @table_columns[table_name] == nil
@table_columns[table_name] = columns(table_name)
end
@table_columns = {} unless @table_columns
@table_columns[table_name] = columns(table_name) if @table_columns[table_name] == nil
@table_columns[table_name].each do |col|
return col.name if col.identity
end
@ -361,17 +377,35 @@ module ActiveRecord
end
def query_contains_identity_column(sql, col)
return sql =~ /[\[.,]\s*#{col}/
return sql =~ /\[#{col}\]/
end
def get_order_by(sql)
return sql, sql.gsub(/\s*DESC\s*/, "").gsub(/\s*ASC\s*/, " DESC")
def change_order_direction(order)
case order
when /DESC/i then order.gsub(/DESC/i, "ASC")
when /ASC/i then order.gsub(/ASC/i, "DESC")
else String.new(order).insert(-1, " DESC")
end
end
def get_offset_amount(limit)
limit = limit.gsub!(/.OFFSET./i, ",").split(',')
return limit[0].to_i, limit[0].to_i+limit[1].to_i
def get_special_columns(table_name)
special = []
@table_columns = {} unless @table_columns
@table_columns[table_name] = columns(table_name) if @table_columns[table_name] == nil
@table_columns[table_name].each do |col|
special << col.name if col.is_special
end
special
end
end
end
end
def repair_special_columns(sql)
special_cols = get_special_columns(get_table_name(sql))
for col in special_cols.to_a
sql.gsub!(Regexp.new(" #{col.to_s} = "), " #{col.to_s} LIKE ")
end
sql
end
end #class SQLServerAdapter < AbstractAdapter
end #module ConnectionAdapters
end #module ActiveRecord

View file

@ -902,6 +902,7 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
no_of_devels = Developer.count
no_of_projects = Project.count
now = Date.today
sqlnow = Time.now.strftime("%Y/%m/%d 00:00:00")
ken = Developer.new("name" => "Ken")
ken.projects.push_with_attributes( Project.find(1), :joined_on => now )
p = Project.new("name" => "Foomatic")
@ -916,7 +917,13 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
assert_equal 2, ken.projects(true).size
kenReloaded = Developer.find_by_name 'Ken'
kenReloaded.projects.each { |prj| assert_equal(now.to_s, prj.joined_on.to_s) }
# SQL Server doesn't have a separate column type just for dates,
# so the time is in the string and incorrectly formatted
if ActiveRecord::ConnectionAdapters.const_defined? :SQLServerAdapter and ActiveRecord::Base.connection.instance_of?(ActiveRecord::ConnectionAdapters::SQLServerAdapter)
kenReloaded.projects.each { |prj| assert_equal(sqlnow, prj.joined_on.to_s) }
else
kenReloaded.projects.each { |prj| assert_equal(now.to_s, prj.joined_on.to_s) }
end
end
def test_build
@ -1004,7 +1011,13 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
end
def test_additional_columns_from_join_table
assert_equal Date.new(2004, 10, 10).to_s, Developer.find(1).projects.first.joined_on.to_s
# SQL Server doesn't have a separate column type just for dates,
# so the time is in the string and incorrectly formatted
if ActiveRecord::ConnectionAdapters.const_defined? :SQLServerAdapter and ActiveRecord::Base.connection.instance_of?(ActiveRecord::ConnectionAdapters::SQLServerAdapter)
assert_equal Time.mktime(2004, 10, 10).strftime("%Y/%m/%d 00:00:00"), Developer.find(1).projects.first.joined_on.to_s
else
assert_equal Date.new(2004, 10, 10).to_s, Developer.find(1).projects.first.joined_on.to_s
end
end
def test_destroy_all
@ -1019,8 +1032,15 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
def test_rich_association
jamis = developers(:jamis)
jamis.projects.push_with_attributes(projects(:action_controller), :joined_on => Date.today)
assert_equal Date.today.to_s, jamis.projects.select { |p| p.name == projects(:action_controller).name }.first.joined_on.to_s
assert_equal Date.today.to_s, developers(:jamis).projects.select { |p| p.name == projects(:action_controller).name }.first.joined_on.to_s
# SQL Server doesn't have a separate column type just for dates,
# so the time is in the string and incorrectly formatted
if ActiveRecord::ConnectionAdapters.const_defined? :SQLServerAdapter and ActiveRecord::Base.connection.instance_of?(ActiveRecord::ConnectionAdapters::SQLServerAdapter)
assert_equal Time.now.strftime("%Y/%m/%d 00:00:00"), jamis.projects.select { |p| p.name == projects(:action_controller).name }.first.joined_on.to_s
assert_equal Time.now.strftime("%Y/%m/%d 00:00:00"), developers(:jamis).projects.select { |p| p.name == projects(:action_controller).name }.first.joined_on.to_s
else
assert_equal Date.today.to_s, jamis.projects.select { |p| p.name == projects(:action_controller).name }.first.joined_on.to_s
assert_equal Date.today.to_s, developers(:jamis).projects.select { |p| p.name == projects(:action_controller).name }.first.joined_on.to_s
end
end
def test_associations_with_conditions

View file

@ -319,6 +319,10 @@ class BasicsTest < Test::Unit::TestCase
end
def test_update_all
# The ADO library doesn't support the number of affected rows
if ActiveRecord::ConnectionAdapters.const_defined? :SQLServerAdapter
return true if ActiveRecord::Base.connection.instance_of?(ActiveRecord::ConnectionAdapters::SQLServerAdapter)
end
assert_equal 2, Topic.update_all("content = 'bulk updated!'")
assert_equal "bulk updated!", Topic.find(1).content
assert_equal "bulk updated!", Topic.find(2).content
@ -337,6 +341,10 @@ class BasicsTest < Test::Unit::TestCase
end
def test_delete_all
# The ADO library doesn't support the number of affected rows
if ActiveRecord::ConnectionAdapters.const_defined? :SQLServerAdapter
return true if ActiveRecord::Base.connection.instance_of?(ActiveRecord::ConnectionAdapters::SQLServerAdapter)
end
assert_equal 2, Topic.delete_all
end

View file

@ -7,6 +7,13 @@ class BinaryTest < Test::Unit::TestCase
end
def test_load_save
# Without using prepared statements, it makes no sense to test
# BLOB data with SQL Server, because the length of a statement is
# limited to 8KB.
if ActiveRecord::ConnectionAdapters.const_defined? :SQLServerAdapter
return true if ActiveRecord::Base.connection.instance_of?(ActiveRecord::ConnectionAdapters::SQLServerAdapter)
end
# Without using prepared statements, it makes no sense to test
# BLOB data with DB2, because the length of a statement is
# limited to 32KB.

View file

@ -0,0 +1,26 @@
print "Using native SQLServer via ODBC\n"
require 'fixtures/course'
require 'logger'
ActiveRecord::Base.logger = Logger.new("debug.log")
dsn1 = 'activerecord_unittest'
dsn2 = 'activerecord_unittest2'
ActiveRecord::Base.establish_connection(
:adapter => "sqlserver",
:mode => "ODBC",
:host => "localhost",
:username => "sa",
:password => "",
:dsn => dsn1
)
Course.establish_connection(
:adapter => "sqlserver",
:mode => "ODBC",
:host => "localhost",
:username => "sa",
:password => "",
:dsn => dsn2
)

View file

@ -112,7 +112,7 @@ class FinderTest < Test::Unit::TestCase
assert_equal first_five_developers, Developer.find_all(nil, 'id ASC', [5])
assert_equal no_developers, Developer.find_all(nil, 'id ASC', [0])
end
def test_find_all_with_limit_and_offset
first_three_developers = Developer.find_all nil, 'id ASC', [3, 0]
second_three_developers = Developer.find_all nil, 'id ASC', [3, 3]
@ -128,14 +128,13 @@ class FinderTest < Test::Unit::TestCase
end
def test_find_all_by_one_attribute_with_options
topics = Topic.find_all_by_content("Have a nice day", nil, "id DESC")
topics = Topic.find_all_by_content("Have a nice day", "id DESC")
assert topics(:first), topics.last
topics = Topic.find_all_by_content("Have a nice day", nil, "id DESC")
topics = Topic.find_all_by_content("Have a nice day", "id DESC")
assert topics(:first), topics.first
end
protected
def bind(statement, *vars)
if vars.first.is_a?(Hash)

View file

@ -21,3 +21,5 @@ DROP TABLE authors;
DROP TABLE tasks;
DROP TABLE categories;
DROP TABLE categories_posts;
DROP TABLE fk_test_has_pd;
DROP TABLE fk_test_has_fk;

View file

@ -2,7 +2,7 @@ CREATE TABLE accounts (
id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
firm_id int default NULL,
credit_limit int default NULL
)
);
CREATE TABLE companies (
id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
@ -12,7 +12,7 @@ CREATE TABLE companies (
name varchar(50) default NULL,
client_of int default NULL,
rating int default 1
)
);
CREATE TABLE topics (
id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
@ -27,7 +27,7 @@ CREATE TABLE topics (
replies_count int default 0,
parent_id int default NULL,
type varchar(50) default NULL
)
);
CREATE TABLE developers (
id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,

View file

@ -6,7 +6,15 @@ class InheritanceTest < Test::Unit::TestCase
fixtures :companies, :projects
def test_a_bad_type_column
#SQLServer need to turn Identity Insert On before manually inserting into the Identity column
if ActiveRecord::ConnectionAdapters.const_defined? :SQLServerAdapter and ActiveRecord::Base.connection.instance_of?(ActiveRecord::ConnectionAdapters::SQLServerAdapter)
Company.connection.execute "SET IDENTITY_INSERT companies ON"
end
Company.connection.insert "INSERT INTO companies (id, type, name) VALUES(100, 'bad_class!', 'Not happening')"
#We then need to turn it back Off before continuing.
if ActiveRecord::ConnectionAdapters.const_defined? :SQLServerAdapter and ActiveRecord::Base.connection.instance_of?(ActiveRecord::ConnectionAdapters::SQLServerAdapter)
Company.connection.execute "SET IDENTITY_INSERT companies OFF"
end
assert_raises(ActiveRecord::SubclassNotFound) { Company.find(100) }
end

View file

@ -707,7 +707,7 @@ class ValidationsTest < Test::Unit::TestCase
def test_validates_numericality_of
Topic.validates_numericality_of( :approved, :allow_nil => true )
["10", "10.0", "10.5", "-10.5", "-0.0001","0090","-090","-090.1",nil,""].each do |v|
t = Topic.create("title" => "numeric test", "content" => "whatever", "approved" => v)
t = Topic.new("title" => "numeric test", "content" => "whatever", "approved" => v)
assert t.valid?, "#{v} not recognized as a number"
# we cannot check this as approved is actually an integer field
#assert_in_delta v.to_f, t.approved, 0.0000001
@ -726,7 +726,7 @@ class ValidationsTest < Test::Unit::TestCase
def test_validates_numericality_of_int
Topic.validates_numericality_of( :approved, :only_integer => true, :allow_nil => true )
["42", "+42", "-42", "042", "0042", "-042", 42, nil,""].each do |v|
t = Topic.create("title" => "numeric test", "content" => "whatever", "approved" => v)
t = Topic.new("title" => "numeric test", "content" => "whatever", "approved" => v)
assert t.valid?, "#{v} not recognized as integer"
assert_equal((v.nil? or v == "")? nil : v.to_i, t.approved)
end