aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Heinemeier Hansson <david@loudthinking.com>2005-07-01 17:20:04 +0000
committerDavid Heinemeier Hansson <david@loudthinking.com>2005-07-01 17:20:04 +0000
commitf2a29ca43cb0de38a25bf7f68bca5c11871692ce (patch)
tree49fafc3247a77dc1b8797e4425b28923af6a51d0
parent64612db7cf85aee8434e7b9b4fd8d6d0249c60e4 (diff)
downloadrails-f2a29ca43cb0de38a25bf7f68bca5c11871692ce.tar.gz
rails-f2a29ca43cb0de38a25bf7f68bca5c11871692ce.tar.bz2
rails-f2a29ca43cb0de38a25bf7f68bca5c11871692ce.zip
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
-rw-r--r--activerecord/CHANGELOG4
-rwxr-xr-xactiverecord/Rakefile2
-rwxr-xr-xactiverecord/lib/active_record/base.rb6
-rwxr-xr-xactiverecord/lib/active_record/connection_adapters/abstract_adapter.rb20
-rwxr-xr-xactiverecord/lib/active_record/connection_adapters/mysql_adapter.rb14
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb222
-rwxr-xr-xactiverecord/test/associations_test.rb28
-rwxr-xr-xactiverecord/test/base_test.rb8
-rw-r--r--activerecord/test/binary_test.rb7
-rw-r--r--activerecord/test/connections/native_sqlserver_odbc/connection.rb26
-rwxr-xr-xactiverecord/test/deprecated_finder_test.rb7
-rw-r--r--activerecord/test/fixtures/db_definitions/sqlserver.drop.sql2
-rw-r--r--activerecord/test/fixtures/db_definitions/sqlserver.sql6
-rwxr-xr-xactiverecord/test/inheritance_test.rb8
-rwxr-xr-xactiverecord/test/validations_test.rb4
15 files changed, 234 insertions, 130 deletions
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index cbbf33318a..79968297b8 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -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]
diff --git a/activerecord/Rakefile b/activerecord/Rakefile
index 6ceefb5522..57b583b007 100755
--- a/activerecord/Rakefile
+++ b/activerecord/Rakefile
@@ -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"
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 0060f694a2..69c19cb1fb 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -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.
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 2be587dc79..0503dbebea 100755
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -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
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index 89633639d3..c11ce3772a 100755
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -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)
diff --git a/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb
index 03f5b5ee2b..dd5a7eb39f 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb
@@ -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.
+#
+# 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:
#
-# I have tested this code on a WindowsXP Pro SP1 system,
-# ruby 1.8.2 (2004-07-29) [i386-mswin32], SQL Server 2000.
+# * <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 \ No newline at end of file
+
+ 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
diff --git a/activerecord/test/associations_test.rb b/activerecord/test/associations_test.rb
index 0218f7e6c7..e7160c9365 100755
--- a/activerecord/test/associations_test.rb
+++ b/activerecord/test/associations_test.rb
@@ -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
diff --git a/activerecord/test/base_test.rb b/activerecord/test/base_test.rb
index 1c5702741e..b6ae403ca0 100755
--- a/activerecord/test/base_test.rb
+++ b/activerecord/test/base_test.rb
@@ -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
diff --git a/activerecord/test/binary_test.rb b/activerecord/test/binary_test.rb
index d635621cef..ba43524264 100644
--- a/activerecord/test/binary_test.rb
+++ b/activerecord/test/binary_test.rb
@@ -8,6 +8,13 @@ class BinaryTest < Test::Unit::TestCase
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.
if ActiveRecord::ConnectionAdapters.const_defined? :DB2Adapter
diff --git a/activerecord/test/connections/native_sqlserver_odbc/connection.rb b/activerecord/test/connections/native_sqlserver_odbc/connection.rb
new file mode 100644
index 0000000000..b156a07920
--- /dev/null
+++ b/activerecord/test/connections/native_sqlserver_odbc/connection.rb
@@ -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
+)
diff --git a/activerecord/test/deprecated_finder_test.rb b/activerecord/test/deprecated_finder_test.rb
index 27a0a66253..0111cff3ac 100755
--- a/activerecord/test/deprecated_finder_test.rb
+++ b/activerecord/test/deprecated_finder_test.rb
@@ -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)
diff --git a/activerecord/test/fixtures/db_definitions/sqlserver.drop.sql b/activerecord/test/fixtures/db_definitions/sqlserver.drop.sql
index efa98fe500..226544392d 100644
--- a/activerecord/test/fixtures/db_definitions/sqlserver.drop.sql
+++ b/activerecord/test/fixtures/db_definitions/sqlserver.drop.sql
@@ -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;
diff --git a/activerecord/test/fixtures/db_definitions/sqlserver.sql b/activerecord/test/fixtures/db_definitions/sqlserver.sql
index 9c5658e387..1177b37035 100644
--- a/activerecord/test/fixtures/db_definitions/sqlserver.sql
+++ b/activerecord/test/fixtures/db_definitions/sqlserver.sql
@@ -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,
diff --git a/activerecord/test/inheritance_test.rb b/activerecord/test/inheritance_test.rb
index 3ac29b73c8..dbbbb6eee7 100755
--- a/activerecord/test/inheritance_test.rb
+++ b/activerecord/test/inheritance_test.rb
@@ -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
diff --git a/activerecord/test/validations_test.rb b/activerecord/test/validations_test.rb
index 2ff618b0e6..576d1f6c3d 100755
--- a/activerecord/test/validations_test.rb
+++ b/activerecord/test/validations_test.rb
@@ -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