Skip to content

Commit 05878fd

Browse files
committed
[Rails3] Support all possible ODBC namespaces. Test helpers for specific connection modes. Rakefile updates for rvm tasks that install both odbc and odbc_utf8. Note current rails 3 patches to run tests.
1 parent 3bded44 commit 05878fd

File tree

8 files changed

+123
-83
lines changed

8 files changed

+123
-83
lines changed

RUNNING_UNIT_TESTS

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,11 @@ be able to run "rake test" which will load all the adapter tests followed by the
3737
core ActiveRecord tests. Lastly, it is typically good practice to do your work
3838
on a local branch of our remote tracking branch.
3939

40+
41+
= Current Patches
42+
43+
These patches may need to be applied in a local topic branch of whatever stable
44+
branch you are testing of rails.
45+
46+
* 3-0-stable https://rails.lighthouseapp.com/projects/8994/tickets/5333
47+

Rakefile

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ namespace :rvm do
4040
'ruby-1.8.6' => {:alias => 'sqlsvr186', :odbc => '0.99991'},
4141
'ruby-1.8.7' => {:alias => 'sqlsvr187', :odbc => '0.99991'},
4242
'ruby-1.9.1' => {:alias => 'sqlsvr191', :odbc => '0.99991'},
43-
'ruby-1.9.2-rc2' => {:alias => 'sqlsvr192', :odbc => '0.99992pre3'},
43+
'ruby-1.9.2' => {:alias => 'sqlsvr192', :odbc => '0.99992pre3'},
4444
'ree-1.8.7' => {:alias => 'sqlsvrree', :odbc => '0.99991'}
4545
}
4646

@@ -98,11 +98,13 @@ namespace :rvm do
9898
RVM.run "curl -O http://www.ch-werner.de/rubyodbc/#{odbc}.tar.gz"
9999
puts "info: RubyODBC extracting clean work directory..."
100100
RVM.run "tar -xf #{odbc}.tar.gz"
101-
RVM.chdir("#{odbc}/ext/utf8") do
102-
puts "info: RubyODBC configuring..."
103-
RVM.ruby 'extconf.rb', "--with-odbc-dir=#{rvm_odbc_dir}"
104-
puts "info: RubyODBC make and installing for #{rvm_current_name}..."
105-
RVM.run "make && make install"
101+
['ext','ext/utf8'].each do |extdir|
102+
RVM.chdir("#{odbc}/#{extdir}") do
103+
puts "info: RubyODBC configuring in #{extdir}..."
104+
RVM.ruby 'extconf.rb', "--with-odbc-dir=#{rvm_odbc_dir}"
105+
puts "info: RubyODBC make and installing for #{rvm_current_name}..."
106+
RVM.run "make && make install"
107+
end
106108
end
107109
end
108110
end
@@ -111,7 +113,7 @@ namespace :rvm do
111113
desc "Install development gems using bundler to each rubie version, installing bundler if not already."
112114
task :bundle => :setup do
113115
rvm_each_rubie do
114-
rvm_install_gem 'bundler', '1.0.0.rc.3'
116+
rvm_install_gem 'bundler', '1.0.0.rc.5'
115117
RVM.run 'bundle install'
116118
end
117119
end

lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,42 @@ module CoreExt
55
module ODBC
66

77
module TimeStamp
8+
89
def to_sqlserver_string
910
date, time, nanoseconds = to_s.split(' ')
1011
"#{date} #{time}.#{sprintf("%03d",nanoseconds.to_i/1000000)}"
1112
end
13+
1214
end
1315

1416
module Statement
17+
1518
def finished?
1619
begin
1720
connected?
1821
false
19-
rescue ::ODBC::Error => e
22+
rescue *Database.parent_modules_error_exceptions
2023
true
2124
end
2225
end
26+
2327
end
2428

2529
module Database
30+
31+
def self.parent_modules
32+
@parent_module ||= ['ODBC','ODBC_UTF8','ODBC_NONE'].map{ |odbc_ns| odbc_ns.constantize rescue nil }.compact
33+
end
34+
35+
def self.parent_modules_error_exceptions
36+
@parent_modules_error_exceptions ||= parent_modules.map { |odbc_ns| "::#{odbc_ns}::Error".constantize }
37+
end
38+
2639
def run_block(*args)
2740
yield sth = run(*args)
2841
sth.drop
2942
end
43+
3044
end
3145

3246
end
@@ -35,8 +49,9 @@ def run_block(*args)
3549
end
3650
end
3751

38-
39-
ODBC::TimeStamp.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ODBC::TimeStamp if defined?(ODBC::TimeStamp)
40-
ODBC::Statement.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ODBC::Statement if defined?(ODBC::Statement)
41-
ODBC::Database.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ODBC::Database if defined?(ODBC::Database)
52+
['ODBC','ODBC_UTF8','ODBC_NONE'].map{ |odbc_ns| odbc_ns.constantize rescue nil }.compact.each do |ns|
53+
ns::TimeStamp.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ODBC::TimeStamp
54+
ns::Statement.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ODBC::Statement
55+
ns::Database.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ODBC::Database
56+
end
4257

lib/active_record/connection_adapters/sqlserver/database_statements.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,9 @@ def zip_fields_and_rows(fields, rows)
189189

190190
# === SQLServer Specific (Executing) ============================ #
191191

192-
def do_execute(sql,name=nil)
193-
log(sql, name || 'EXECUTE') do
192+
def do_execute(sql, name = nil)
193+
name ||= 'EXECUTE'
194+
log(sql, name) do
194195
with_auto_reconnect { raw_connection_do(sql) }
195196
end
196197
end
@@ -255,7 +256,7 @@ def handle_to_fields_and_rows_odbc(handle)
255256
end
256257
rows = results.inject([]) do |rows,row|
257258
row.each_with_index do |value, i|
258-
if value.is_a? ODBC::TimeStamp
259+
if value.is_a? raw_connection.class.parent::TimeStamp
259260
row[i] = value.to_sqlserver_string
260261
end
261262
end

lib/active_record/connection_adapters/sqlserver/errors.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module Sqlserver
88
module Errors
99

1010
LOST_CONNECTION_EXCEPTIONS = {
11-
:odbc => ['ODBC::Error'],
11+
:odbc => ['ODBC::Error','ODBC_UTF8::Error','ODBC_NONE::Error'],
1212
:adonet => ['TypeError','System::Data::SqlClient::SqlException']
1313
}.freeze
1414

@@ -20,7 +20,7 @@ module Errors
2020

2121
def lost_connection_exceptions
2222
exceptions = LOST_CONNECTION_EXCEPTIONS[connection_mode]
23-
@lost_connection_exceptions ||= exceptions ? exceptions.map(&:constantize) : []
23+
@lost_connection_exceptions ||= exceptions ? exceptions.map{ |e| e.constantize rescue nil }.compact : []
2424
end
2525

2626
def lost_connection_messages

lib/active_record/connection_adapters/sqlserver_adapter.rb

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,18 @@ def self.sqlserver_connection(config) #:nodoc:
2020
mode = config[:mode].to_s.downcase.underscore.to_sym
2121
case mode
2222
when :odbc
23-
require_library_or_gem 'odbc' unless defined?(ODBC)
24-
require 'active_record/connection_adapters/sqlserver/core_ext/odbc'
2523
raise ArgumentError, 'Missing :dsn configuration.' unless config.has_key?(:dsn)
24+
if RUBY_VERSION < '1.9'
25+
require_library_or_gem 'odbc'
26+
else
27+
begin
28+
# TODO: [ODBC] Change this to 'odbc_utf8'
29+
require_library_or_gem 'odbc'
30+
rescue LoadError
31+
require_library_or_gem 'odbc'
32+
end
33+
end unless ['::ODBC','::ODBC_UTF8','::ODBC_NONE'].any? { |odbc_ns| odbc_ns.constantize rescue nil }
34+
require 'active_record/connection_adapters/sqlserver/core_ext/odbc'
2635
when :adonet
2736
require 'System.Data'
2837
raise ArgumentError, 'Missing :database configuration.' unless config.has_key?(:database)
@@ -315,7 +324,7 @@ def cs_equality_operator
315324
@@cs_equality_operator || 'COLLATE Latin1_General_CS_AS_WS ='
316325
end
317326

318-
327+
319328
protected
320329

321330
# === Abstract Adapter (Misc Support) =========================== #
@@ -339,7 +348,8 @@ def connect
339348
config = @connection_options
340349
@connection = case connection_mode
341350
when :odbc
342-
ODBC.connect config[:dsn], config[:username], config[:password]
351+
odbc = ['::ODBC','::ODBC_UTF8','::ODBC_NONE'].detect{ |odbc_ns| odbc_ns.constantize rescue nil }.constantize
352+
odbc.connect config[:dsn], config[:username], config[:password]
343353
when :adonet
344354
System::Data::SqlClient::SqlConnection.new.tap do |connection|
345355
connection.connection_string = System::Data::SqlClient::SqlConnectionStringBuilder.new.tap do |cs|

test/cases/connection_test_sqlserver.rb

Lines changed: 64 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,6 @@ def setup
1212
end
1313

1414

15-
should 'return finished ODBC statment handle from #execute without block' do
16-
assert_all_statements_used_are_closed do
17-
@connection.execute('SELECT * FROM [topics]')
18-
end
19-
end
20-
21-
should 'finish ODBC statment handle from #execute with block' do
22-
assert_all_statements_used_are_closed do
23-
@connection.execute('SELECT * FROM [topics]') { }
24-
end
25-
end
26-
27-
should 'finish connection from #raw_select' do
28-
assert_all_statements_used_are_closed do
29-
@connection.send(:raw_select,'SELECT * FROM [topics]')
30-
end
31-
end
32-
3315
should 'affect rows' do
3416
topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } }
3517
updated = Topic.update(topic_data.keys, topic_data.values)
@@ -39,38 +21,6 @@ def setup
3921
assert_equal 2, Topic.delete([1, 2])
4022
end
4123

42-
should 'execute without block closes statement' do
43-
assert_all_statements_used_are_closed do
44-
@connection.execute("SELECT 1")
45-
end
46-
end
47-
48-
should 'execute with block closes statement' do
49-
assert_all_statements_used_are_closed do
50-
@connection.execute("SELECT 1") do |sth|
51-
assert !sth.finished?, "Statement should still be alive within block"
52-
end
53-
end
54-
end
55-
56-
should 'insert with identity closes statement' do
57-
assert_all_statements_used_are_closed do
58-
@connection.insert("INSERT INTO accounts ([id], [firm_id],[credit_limit]) values (999, 1, 50)")
59-
end
60-
end
61-
62-
should 'insert without identity closes statement' do
63-
assert_all_statements_used_are_closed do
64-
@connection.insert("INSERT INTO accounts ([firm_id],[credit_limit]) values (1, 50)")
65-
end
66-
end
67-
68-
should 'active closes statement' do
69-
assert_all_statements_used_are_closed do
70-
@connection.active?
71-
end
72-
end
73-
7424
should 'allow usage of :database connection option to remove setting from dsn' do
7525
assert_equal 'activerecord_unittest', @connection.current_database
7626
begin
@@ -82,6 +32,61 @@ def setup
8232
end
8333
end
8434

35+
context 'ODBC connection management' do
36+
37+
should 'return finished ODBC statement handle from #execute without block' do
38+
assert_all_odbc_statements_used_are_closed do
39+
@connection.execute('SELECT * FROM [topics]')
40+
end
41+
end
42+
43+
should 'finish ODBC statement handle from #execute with block' do
44+
assert_all_odbc_statements_used_are_closed do
45+
@connection.execute('SELECT * FROM [topics]') { }
46+
end
47+
end
48+
49+
should 'finish connection from #raw_select' do
50+
assert_all_odbc_statements_used_are_closed do
51+
@connection.send(:raw_select,'SELECT * FROM [topics]')
52+
end
53+
end
54+
55+
should 'execute without block closes statement' do
56+
assert_all_odbc_statements_used_are_closed do
57+
@connection.execute("SELECT 1")
58+
end
59+
end
60+
61+
should 'execute with block closes statement' do
62+
assert_all_odbc_statements_used_are_closed do
63+
@connection.execute("SELECT 1") do |sth|
64+
assert !sth.finished?, "Statement should still be alive within block"
65+
end
66+
end
67+
end
68+
69+
should 'insert with identity closes statement' do
70+
assert_all_odbc_statements_used_are_closed do
71+
@connection.insert("INSERT INTO accounts ([id], [firm_id],[credit_limit]) values (999, 1, 50)")
72+
end
73+
end
74+
75+
should 'insert without identity closes statement' do
76+
assert_all_odbc_statements_used_are_closed do
77+
@connection.insert("INSERT INTO accounts ([firm_id],[credit_limit]) values (1, 50)")
78+
end
79+
end
80+
81+
should 'active closes statement' do
82+
assert_all_odbc_statements_used_are_closed do
83+
@connection.active?
84+
end
85+
end
86+
87+
end if connection_mode_odbc?
88+
89+
8590
context 'Connection management' do
8691

8792
setup do
@@ -116,21 +121,18 @@ def setup
116121

117122
private
118123

119-
def assert_all_statements_used_are_closed(&block)
124+
def assert_all_odbc_statements_used_are_closed(&block)
125+
odbc = @connection.raw_connection.class.parent
120126
existing_handles = []
121-
ObjectSpace.each_object(ODBC::Statement) {|handle| existing_handles << handle}
122-
GC.disable
127+
ObjectSpace.each_object(odbc::Statement) { |h| existing_handles << h }
128+
existing_handle_ids = existing_handles.map(&:object_id)
129+
assert existing_handles.all?(&:finished?), "Somewhere before the block some statements were not closed"
130+
GC.disable
123131
yield
124132
used_handles = []
125-
ObjectSpace.each_object(ODBC::Statement) {|handle| used_handles << handle unless existing_handles.include? handle}
126-
assert_block "No statements were used within given block" do
127-
used_handles.size > 0
128-
end
129-
ObjectSpace.each_object(ODBC::Statement) do |handle|
130-
assert_block "Statement should have been closed within given block" do
131-
handle.finished?
132-
end
133-
end
133+
ObjectSpace.each_object(odbc::Statement) { |h| used_handles << h unless existing_handle_ids.include?(h.object_id) }
134+
assert used_handles.size > 0, "No statements were used within given block"
135+
assert used_handles.all?(&:finished?), "Statement should have been closed within given block"
134136
ensure
135137
GC.enable
136138
end

test/cases/sqlserver_helper.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ def raw_select_with_query_record(sql, name = nil)
9191
module ActiveRecord
9292
class TestCase < ActiveSupport::TestCase
9393
class << self
94+
def connection_mode_odbc? ; ActiveRecord::Base.connection.send(:connection_mode) == :odbc ; end
9495
def sqlserver_2005? ; ActiveRecord::Base.connection.sqlserver_2005? ; end
9596
def sqlserver_2008? ; ActiveRecord::Base.connection.sqlserver_2008? ; end
9697
def ruby_19? ; RUBY_VERSION >= '1.9' ; end
@@ -106,6 +107,7 @@ def assert_sql(*patterns_to_match)
106107
end
107108
assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map(&:inspect).join(', ')} not found in:\n#{$queries_executed.inspect}"
108109
end
110+
def connection_mode_odbc? ; self.class.connection_mode_odbc? ; end
109111
def sqlserver_2005? ; self.class.sqlserver_2005? ; end
110112
def sqlserver_2008? ; self.class.sqlserver_2008? ; end
111113
def ruby_19? ; self.class.ruby_19? ; end

0 commit comments

Comments
 (0)