Skip to content

Commit 366a97e

Browse files
committed
Switch to better abstract execute that mimics PostgreSQL adapter by using #raw_select as single method for type casting straigt DBI values. This kept #select_rows from duplicating work. Added a alias method chain to #raw_select since #select (and maybe more) will use it. Note, some thing may look odd, but I have to point out that DBI's execute does not return the handle when a block is given... a bug in my opinion.
1 parent 8b03991 commit 366a97e

File tree

4 files changed

+104
-44
lines changed

4 files changed

+104
-44
lines changed

lib/active_record/connection_adapters/sqlserver_adapter.rb

Lines changed: 61 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ def quoted_false
357357
end
358358

359359
# TODO: I get the feeling this needs to go and that it is patching something else wrong.
360-
def quoted_date(value)
360+
def quoted_date(value)
361361
if value.acts_like?(:time)
362362
value.strftime("%Y%m%d %H:%M:%S")
363363
elsif value.acts_like?(:date)
@@ -375,40 +375,16 @@ def quoted_date(value)
375375
# DATABASE STATEMENTS ======================================
376376

377377
def select_rows(sql, name = nil)
378-
rows = []
379-
repair_special_columns(sql)
380-
log(sql, name) do
381-
@connection.select_all(sql) do |row|
382-
record = []
383-
row.each do |col|
384-
if col.is_a? DBI::Timestamp
385-
record << col.to_time
386-
else
387-
record << col
388-
end
389-
end
390-
rows << record
391-
end
392-
end
393-
rows
378+
raw_select(sql,name).last
394379
end
395380

396-
def execute(sql, name = nil)
381+
def execute(sql, name = nil, &block)
397382
if table_name = query_requires_identity_insert?(sql)
398-
log(sql, name) do
399-
with_identity_insert_enabled(table_name) do
400-
@connection.execute(sql) do |handle|
401-
yield(handle) if block_given?
402-
end
403-
end
404-
end
383+
handle = with_identity_insert_enabled(table_name) { raw_execute(sql,name,&block) }
405384
else
406-
log(sql, name) do
407-
@connection.execute(sql) do |handle|
408-
yield(handle) if block_given?
409-
end
410-
end
385+
handle = raw_execute(sql,name,&block)
411386
end
387+
finish_statement_handle(handle)
412388
end
413389

414390
def begin_db_transaction
@@ -511,18 +487,14 @@ def empty_insert_statement(table_name)
511487

512488
def select(sql, name = nil, ignore_special_columns = false)
513489
repair_special_columns(sql) unless ignore_special_columns
490+
fields, rows = raw_select(sql,name)
514491
result = []
515-
execute(sql) do |handle|
516-
handle.each do |row|
517-
row_hash = {}
518-
row.each_with_index do |value, i|
519-
if value.is_a? DBI::Timestamp
520-
value = DateTime.new(value.year, value.month, value.day, value.hour, value.minute, value.sec)
521-
end
522-
row_hash[handle.column_names[i]] = value
523-
end
524-
result << row_hash
492+
for row in rows
493+
row_hash = {}
494+
fields.each_with_index do |f, i|
495+
row_hash[f] = row[i]
525496
end
497+
result << row_hash
526498
end
527499
result
528500
end
@@ -777,6 +749,11 @@ def disconnect!
777749
@connection.disconnect rescue nil
778750
end
779751

752+
def finish_statement_handle(handle)
753+
handle.finish if handle && handle.respond_to?(:finish) && !handle.finished?
754+
handle
755+
end
756+
780757

781758
# RAKE UTILITY METHODS =====================================#
782759

@@ -828,6 +805,48 @@ def remove_database_connections_and_rollback(name)
828805

829806
private
830807

808+
# DATABASE STATEMENTS ======================================
809+
810+
def raw_execute(sql, name = nil, &block)
811+
log(sql, name) do
812+
if block_given?
813+
@connection.execute(sql) { |handle| yield(handle) }
814+
else
815+
@connection.execute(sql)
816+
end
817+
end
818+
end
819+
820+
def raw_select(sql, name = nil)
821+
handle = raw_execute(sql,name)
822+
fields = handle.column_names
823+
results = handle_as_array(handle)
824+
rows = results.inject([]) do |rows,row|
825+
row.each_with_index do |value, i|
826+
if value.is_a? DBI::Timestamp
827+
row[i] = value.to_time
828+
# TODO: Let's revisit this and the whole DB time thing.
829+
# row[i] = DateTime.new(value.year, value.month, value.day, value.hour, value.minute, value.sec)
830+
end
831+
end
832+
rows << row
833+
end
834+
return fields, rows
835+
end
836+
837+
def query(sql, name = nil)
838+
handle = raw_execute(sql,name)
839+
handle_as_array(handle)
840+
end
841+
842+
def handle_as_array(handle)
843+
array = handle.inject([]) do |rows,row|
844+
rows << row.inject([]){ |values,value| values << value }
845+
end
846+
finish_statement_handle(handle)
847+
array
848+
end
849+
831850
# IDENTITY INSERTS =========================================#
832851

833852
def with_identity_insert_enabled(table_name, &block)
@@ -838,7 +857,8 @@ def with_identity_insert_enabled(table_name, &block)
838857
end
839858

840859
def set_identity_insert(table_name, enable = true)
841-
execute "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}"
860+
sql = "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}"
861+
log(sql,'IDENTITY_INSERT') { execute(sql) }
842862
rescue Exception => e
843863
raise ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}"
844864
end

test/cases/adapter_test_sqlserver.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
require 'cases/sqlserver_helper'
22
require 'models/task'
33
require 'models/topic'
4+
require 'models/reply'
45
require 'models/joke'
56
require 'models/subscriber'
67

test/cases/connection_test_sqlserver.rb

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,36 @@ def setup
1313
end
1414

1515

16+
should 'return finished DBI statment handle from #execute without block' do
17+
handle = @connection.execute('SELECT * FROM [topics]')
18+
assert_instance_of DBI::StatementHandle, handle
19+
assert handle.finished?
20+
end
21+
22+
should 'finish DBI statment handle from #execute with block' do
23+
assert_all_statements_used_are_closed do
24+
@connection.execute('SELECT * FROM [topics]') { }
25+
end
26+
end
27+
28+
should 'return an unfinished DBI statement handler from #raw_execute' do
29+
handle = @connection.send(:raw_execute,'SELECT * FROM [topics]')
30+
assert_instance_of DBI::StatementHandle, handle
31+
assert !handle.finished?
32+
end
33+
34+
should 'finish connection from #query' do
35+
assert_all_statements_used_are_closed do
36+
@connection.send(:query,'SELECT * FROM [topics]')
37+
end
38+
end
39+
40+
should 'finish connection from #raw_select' do
41+
assert_all_statements_used_are_closed do
42+
@connection.send(:raw_select,'SELECT * FROM [topics]')
43+
end
44+
end
45+
1646
should 'affect rows' do
1747
assert Topic.connection.instance_variable_get("@connection")["AutoCommit"]
1848
topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } }
@@ -28,21 +58,21 @@ def setup
2858
@connection.execute("SELECT 1")
2959
end
3060
end
31-
61+
3262
should 'execute with block closes statement' do
3363
assert_all_statements_used_are_closed do
3464
@connection.execute("SELECT 1") do |sth|
3565
assert !sth.finished?, "Statement should still be alive within block"
3666
end
3767
end
3868
end
39-
69+
4070
should 'insert with identity closes statement' do
4171
assert_all_statements_used_are_closed do
4272
@connection.insert("INSERT INTO accounts ([id], [firm_id],[credit_limit]) values (999, 1, 50)")
4373
end
4474
end
45-
75+
4676
should 'insert without identity closes statement' do
4777
assert_all_statements_used_are_closed do
4878
@connection.insert("INSERT INTO accounts ([firm_id],[credit_limit]) values (1, 50)")

test/cases/sqlserver_helper.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@ class TableWithRealColumn < ActiveRecord::Base; end
1818
IGNORED_SQL << /SELECT SCOPE_IDENTITY/ << /INFORMATION_SCHEMA.TABLES/ << /INFORMATION_SCHEMA.COLUMNS/
1919
end
2020

21+
ActiveRecord::ConnectionAdapters::SQLServerAdapter.class_eval do
22+
def raw_select_with_query_record(sql, name = nil)
23+
$queries_executed ||= []
24+
$queries_executed << sql unless IGNORED_SQL.any? { |r| sql =~ r }
25+
raw_select_without_query_record(sql,name)
26+
end
27+
alias_method_chain :raw_select, :query_record
28+
end
29+
2130
module ActiveRecord
2231
class TestCase < ActiveSupport::TestCase
2332
def assert_sql(*patterns_to_match)

0 commit comments

Comments
 (0)