Skip to content

Commit 321a154

Browse files
committed
Support upcoming ruby-odbc 0.99992 which returns native datetime objects and can use local/utc time.
1 parent 6fac0a4 commit 321a154

File tree

6 files changed

+100
-52
lines changed

6 files changed

+100
-52
lines changed

lib/active_record/connection_adapters/sqlserver/database_statements.rb

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -254,29 +254,40 @@ def handle_to_names_and_values(handle, options={})
254254
end
255255

256256
def handle_to_names_and_values_odbc(handle, options={})
257+
@connection.use_utc = ActiveRecord::Base.default_timezone == :utc if @connection_supports_native_types
257258
case options[:fetch]
258259
when :all, :one
259-
rows = if options[:fetch] == :all
260-
handle.fetch_all || []
261-
else
262-
row = handle.fetch
263-
row ? [row] : [[]]
264-
end
265-
names = handle.columns(true).map{ |c| c.name }
266-
names_and_values = []
267-
rows.each do |row|
268-
h = {}
269-
i = 0
270-
while i < row.size
271-
v = row[i]
272-
h[names[i]] = v.respond_to?(:to_sqlserver_string) ? v.to_sqlserver_string : v
273-
i += 1
260+
if @connection_supports_native_types
261+
if options[:fetch] == :all
262+
handle.each_hash || []
263+
else
264+
row = handle.fetch_hash
265+
rows = row ? [row] : [[]]
266+
end
267+
else
268+
rows = if options[:fetch] == :all
269+
handle.fetch_all || []
270+
else
271+
row = handle.fetch
272+
row ? [row] : [[]]
273+
end
274+
names = handle.columns(true).map{ |c| c.name }
275+
names_and_values = []
276+
rows.each do |row|
277+
h = {}
278+
i = 0
279+
while i < row.size
280+
v = row[i]
281+
h[names[i]] = v.respond_to?(:to_sqlserver_string) ? v.to_sqlserver_string : v
282+
i += 1
283+
end
284+
names_and_values << h
274285
end
275-
names_and_values << h
286+
names_and_values
276287
end
277-
names_and_values
278288
when :rows
279289
rows = handle.fetch_all || []
290+
return rows if @connection_supports_native_types
280291
rows.each do |row|
281292
i = 0
282293
while i < row.size

lib/active_record/connection_adapters/sqlserver_adapter.rb

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,8 @@ class SQLServerAdapter < AbstractAdapter
185185
DATABASE_VERSION_REGEXP = /Microsoft SQL Server\s+(\d{4})/
186186
SUPPORTED_VERSIONS = [2005,2008].freeze
187187

188-
attr_reader :database_version, :database_year
188+
attr_reader :database_version, :database_year,
189+
:connection_supports_native_types
189190

190191
cattr_accessor :native_text_database_type, :native_binary_database_type, :native_string_database_type,
191192
:log_info_schema_queries, :enable_default_unicode_types, :auto_connect,
@@ -352,7 +353,13 @@ def connect
352353
@connection = case @connection_options[:mode]
353354
when :odbc
354355
odbc = ['::ODBC','::ODBC_UTF8','::ODBC_NONE'].detect{ |odbc_ns| odbc_ns.constantize rescue nil }.constantize
355-
odbc.connect config[:dsn], config[:username], config[:password]
356+
odbc.connect(config[:dsn], config[:username], config[:password]).tap do |c|
357+
if c.respond_to?(:use_time)
358+
c.use_time = true
359+
c.use_utc = ActiveRecord::Base.default_timezone == :utc
360+
@connection_supports_native_types = true
361+
end
362+
end
356363
when :adonet
357364
System::Data::SqlClient::SqlConnection.new.tap do |connection|
358365
connection.connection_string = System::Data::SqlClient::SqlConnectionStringBuilder.new.tap do |cs|

test/cases/adapter_test_sqlserver.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,13 +192,13 @@ def setup
192192

193193
should 'find 003 millisecond in the DB with before and after casting' do
194194
existing_003 = SqlServerChronic.find_by_datetime!(@db_datetime_003)
195-
assert_equal @db_datetime_003, existing_003.datetime_before_type_cast
195+
assert_equal @db_datetime_003, existing_003.datetime_before_type_cast if existing_003.datetime_before_type_cast.is_a?(String)
196196
assert_equal 3000, existing_003.datetime.usec, 'A 003 millisecond in SQL Server is 3000 microseconds'
197197
end
198198

199199
should 'find 123 millisecond in the DB with before and after casting' do
200200
existing_123 = SqlServerChronic.find_by_datetime!(@db_datetime_123)
201-
assert_equal @db_datetime_123, existing_123.datetime_before_type_cast
201+
assert_equal @db_datetime_123, existing_123.datetime_before_type_cast if existing_123.datetime_before_type_cast.is_a?(String)
202202
assert_equal 123000, existing_123.datetime.usec, 'A 123 millisecond in SQL Server is 123000 microseconds'
203203
end
204204

@@ -209,14 +209,14 @@ def setup
209209
should 'truncate 123456 usec to just 123 in the DB cast back to 123000' do
210210
@time.stubs(:usec).returns(123456)
211211
saved = SqlServerChronic.create!(:datetime => @time).reload
212-
assert_equal '123', saved.datetime_before_type_cast.split('.')[1]
212+
assert_equal '123', saved.datetime_before_type_cast.split('.')[1] if saved.datetime_before_type_cast.is_a?(String)
213213
assert_equal 123000, saved.datetime.usec
214214
end
215215

216216
should 'truncate 3001 usec to just 003 in the DB cast back to 3000' do
217217
@time.stubs(:usec).returns(3001)
218218
saved = SqlServerChronic.create!(:datetime => @time).reload
219-
assert_equal '003', saved.datetime_before_type_cast.split('.')[1]
219+
assert_equal '003', saved.datetime_before_type_cast.split('.')[1] if saved.datetime_before_type_cast.is_a?(String)
220220
assert_equal 3000, saved.datetime.usec
221221
end
222222

test/cases/attribute_methods_test_sqlserver.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ class AttributeMethodsTest < ActiveRecord::TestCase
1717

1818
fixtures :developers
1919

20-
# Duplicate with BaseTest#test_read_attributes_before_type_cast_on_datetime
21-
# Pick a winer when rails core does the same :/
2220
def test_coerced_read_attributes_before_type_cast_on_datetime
2321
developer = Developer.find(:first)
24-
assert_equal "#{developer.created_at.to_s(:db)}.000" , developer.attributes_before_type_cast["created_at"]
22+
if developer.created_at_before_type_cast.is_a?(String)
23+
assert_equal "#{developer.created_at.to_s(:db)}.000" , developer.attributes_before_type_cast["created_at"]
24+
end
2525
end
2626

2727
def test_coerced_typecast_attribute_from_select_to_false

test/cases/base_test_sqlserver.rb

Lines changed: 0 additions & 25 deletions
This file was deleted.
Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,72 @@
11
require 'cases/sqlserver_helper'
2+
require 'models/developer'
3+
require 'models/project'
4+
require 'models/company'
5+
require 'models/customer'
6+
require 'models/order'
7+
require 'models/categorization'
8+
require 'models/category'
9+
require 'models/post'
10+
require 'models/author'
11+
require 'models/tag'
12+
require 'models/tagging'
13+
require 'models/parrot'
14+
require 'models/pirate'
15+
require 'models/treasure'
16+
require 'models/price_estimate'
17+
require 'models/club'
18+
require 'models/member'
19+
require 'models/membership'
20+
require 'models/sponsor'
21+
require 'models/country'
22+
require 'models/treaty'
23+
require 'active_support/core_ext/string/conversions'
224

325
class HasAndBelongsToManyAssociationsTestSqlserver < ActiveRecord::TestCase
426
end
527

628
class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
729

8-
COERCED_TESTS = [:test_count_with_finder_sql]
30+
COERCED_TESTS = [
31+
:test_count_with_finder_sql,
32+
:test_should_record_timestamp_for_join_table
33+
]
934

1035
include SqlserverCoercedTest
1136

37+
fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects,
38+
:parrots, :pirates, :treasures, :price_estimates, :tags, :taggings
39+
40+
def setup_data_for_habtm_case
41+
ActiveRecord::Base.connection.execute('delete from countries_treaties')
42+
country = Country.new(:name => 'India')
43+
country.country_id = 'c1'
44+
country.save!
45+
treaty = Treaty.new(:name => 'peace')
46+
treaty.treaty_id = 't1'
47+
country.treaties << treaty
48+
end
49+
50+
1251
def test_coerced_count_with_finder_sql
1352
assert true
1453
end
1554

55+
def test_coerced_should_record_timestamp_for_join_table
56+
setup_data_for_habtm_case
57+
con = ActiveRecord::Base.connection
58+
sql = 'select * from countries_treaties'
59+
record = con.select_rows(sql).last
60+
assert_not_nil record[2]
61+
assert_not_nil record[3]
62+
if record[2].is_a?(String)
63+
assert_match %r{\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}}, record[2]
64+
assert_match %r{\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}}, record[3]
65+
else
66+
assert_match %r{\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}}, record[2].to_s(:db)
67+
assert_match %r{\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}}, record[3].to_s(:db)
68+
end
69+
end
70+
1671

1772
end

0 commit comments

Comments
 (0)