Skip to content

Commit c93f08a

Browse files
committed
Restructure the low level raw connections to create column name/value hashes so that a ODBC::Statement#columns is only called once.
1 parent 5476d72 commit c93f08a

File tree

6 files changed

+75
-85
lines changed

6 files changed

+75
-85
lines changed

Diff for: lib/active_record/connection_adapters/sqlserver/database_statements.rb

+51-47
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ module Sqlserver
44
module DatabaseStatements
55

66
def select_rows(sql, name = nil)
7-
raw_select(sql,name).first.last
7+
raw_select sql, name, true
88
end
99

1010
def execute(sql, name = nil, skip_logging = false)
@@ -66,13 +66,21 @@ def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
6666
def execute_procedure(proc_name, *variables)
6767
vars = variables.map{ |v| quote(v) }.join(', ')
6868
sql = "EXEC #{proc_name} #{vars}".strip
69-
select(sql,'Execute Procedure').inject([]) do |results,row|
70-
if row.kind_of?(Array)
71-
results << row.inject([]) { |rs,r| rs << r.with_indifferent_access }
72-
else
73-
results << row.with_indifferent_access
69+
results = []
70+
log(sql,'Execute Procedure') do
71+
raw_connection_run(sql) do |handle|
72+
get_rows = lambda {
73+
rows = handle_to_names_and_values(handle,false)
74+
rows.each_with_index { |r,i| rows[i] = r.with_indifferent_access }
75+
results << rows
76+
}
77+
get_rows.call
78+
while handle_more_results?(handle)
79+
get_rows.call
80+
end
7481
end
7582
end
83+
results.many? ? results : results.first
7684
end
7785

7886
def use_database(database=nil)
@@ -154,12 +162,7 @@ def charset
154162
protected
155163

156164
def select(sql, name = nil)
157-
fields_and_row_sets = raw_select(sql,name)
158-
final_result_set = fields_and_row_sets.inject([]) do |rs,fields_and_rows|
159-
fields, rows = fields_and_rows
160-
rs << zip_fields_and_rows(fields,rows)
161-
end
162-
final_result_set.many? ? final_result_set : final_result_set.first
165+
raw_select(sql,name)
163166
end
164167

165168
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
@@ -177,16 +180,6 @@ def valid_isolation_levels
177180
["READ COMMITTED", "READ UNCOMMITTED", "REPEATABLE READ", "SERIALIZABLE", "SNAPSHOT"]
178181
end
179182

180-
def zip_fields_and_rows(fields, rows)
181-
rows.inject([]) do |results,row|
182-
row_hash = {}
183-
fields.each_with_index do |f, i|
184-
row_hash[f] = row[i]
185-
end
186-
results << row_hash
187-
end
188-
end
189-
190183
# === SQLServer Specific (Executing) ============================ #
191184

192185
def do_execute(sql, name = nil)
@@ -207,26 +200,15 @@ def raw_connection_do(sql)
207200

208201
# === SQLServer Specific (Selecting) ============================ #
209202

210-
def raw_select(sql, name = nil)
211-
fields_and_row_sets = []
203+
def raw_select(sql, name=nil, rows_only=false)
212204
log(sql,name) do
213205
begin
214206
handle = raw_connection_run(sql)
215-
loop do
216-
fields_and_rows = case connection_mode
217-
when :odbc
218-
handle_to_fields_and_rows_odbc(handle)
219-
when :adonet
220-
handle_to_fields_and_rows_adonet(handle)
221-
end
222-
fields_and_row_sets << fields_and_rows
223-
break unless handle_more_results?(handle)
224-
end
207+
handle_to_names_and_values(handle, rows_only)
225208
ensure
226209
finish_statement_handle(handle)
227210
end
228211
end
229-
fields_and_row_sets
230212
end
231213

232214
def raw_connection_run(sql)
@@ -248,24 +230,46 @@ def handle_more_results?(handle)
248230
handle.next_result
249231
end
250232
end
251-
252-
def handle_to_fields_and_rows_odbc(handle)
253-
fields = handle.columns(true).map { |c| c.name }
254-
results = handle.inject([]) do |rows,row|
255-
rows << row.inject([]) { |values,value| values << value }
233+
234+
def handle_to_names_and_values(handle, rows_only)
235+
case connection_mode
236+
when :odbc
237+
handle_to_names_and_values_odbc(handle, rows_only)
238+
when :adonet
239+
handle_to_names_and_values_adonet(handle, rows_only)
256240
end
257-
rows = results.inject([]) do |rows,row|
258-
row.each_with_index do |value, i|
259-
if value.respond_to?(:to_sqlserver_string)
260-
row[i] = value.to_sqlserver_string
241+
end
242+
243+
def handle_to_names_and_values_odbc(handle, rows_only)
244+
rows = handle.fetch_all || []
245+
if rows_only
246+
rows.each do |row|
247+
i = 0
248+
while i < row.size
249+
v = row[i]
250+
row[i] = v.to_sqlserver_string if v.respond_to?(:to_sqlserver_string)
251+
i += 1
261252
end
262253
end
263-
rows << row
254+
rows
255+
else
256+
names = handle.columns(true).map{ |c| c.name }
257+
names_and_values = []
258+
rows.each do |row|
259+
h = {}
260+
i = 0
261+
while i < row.size
262+
v = row[i]
263+
h[names[i]] = v.respond_to?(:to_sqlserver_string) ? v.to_sqlserver_string : v
264+
i += 1
265+
end
266+
names_and_values << h
267+
end
268+
names_and_values
264269
end
265-
[fields,rows]
266270
end
267271

268-
def handle_to_fields_and_rows_adonet(handle)
272+
def handle_to_names_and_values_adonet(handle, rows_only)
269273
if handle.has_rows
270274
fields = []
271275
rows = []

Diff for: lib/active_record/connection_adapters/sqlserver/schema_statements.rb

+19-20
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ def columns(table_name, name = nil)
6060
return [] if table_name.blank?
6161
cache_key = unqualify_table_name(table_name)
6262
@sqlserver_columns_cache[cache_key] ||= column_definitions(table_name).collect do |ci|
63-
sqlserver_options = ci.except(:name,:default_value,:type,:null).merge(:database_year=>database_year)
64-
SQLServerColumn.new ci[:name], ci[:default_value], ci[:type], ci[:null], sqlserver_options
63+
sqlserver_options = ci.except('name','default_value','type','null').merge('database_year'=>database_year)
64+
SQLServerColumn.new ci['name'], ci['default_value'], ci['type'], ci['null'], sqlserver_options
6565
end
6666
end
6767

@@ -185,7 +185,7 @@ def column_definitions(table_name)
185185
CASE
186186
WHEN columns.IS_NULLABLE = 'YES' THEN 1
187187
ELSE NULL
188-
end as is_nullable,
188+
END as is_nullable,
189189
CASE
190190
WHEN COLUMNPROPERTY(OBJECT_ID(columns.TABLE_SCHEMA+'.'+columns.TABLE_NAME), columns.COLUMN_NAME, 'IsIdentity') = 0 THEN NULL
191191
ELSE 1
@@ -196,31 +196,30 @@ def column_definitions(table_name)
196196
}.gsub(/[ \t\r\n]+/,' ')
197197
results = info_schema_query { select(sql,nil) }
198198
results.collect do |ci|
199-
ci.symbolize_keys!
200-
ci[:type] = case ci[:type]
201-
when /^bit|image|text|ntext|datetime$/
202-
ci[:type]
203-
when /^numeric|decimal$/i
204-
"#{ci[:type]}(#{ci[:numeric_precision]},#{ci[:numeric_scale]})"
205-
when /^char|nchar|varchar|nvarchar|varbinary|bigint|int|smallint$/
206-
ci[:length].to_i == -1 ? "#{ci[:type]}(max)" : "#{ci[:type]}(#{ci[:length]})"
207-
else
208-
ci[:type]
209-
end
210-
if ci[:default_value].nil? && views.include?(table_name)
199+
ci['type'] = case ci['type']
200+
when /^bit|image|text|ntext|datetime$/
201+
ci['type']
202+
when /^numeric|decimal$/i
203+
"#{ci['type']}(#{ci['numeric_precision']},#{ci['numeric_scale']})"
204+
when /^char|nchar|varchar|nvarchar|varbinary|bigint|int|smallint$/
205+
ci['length'].to_i == -1 ? "#{ci['type']}(max)" : "#{ci['type']}(#{ci['length']})"
206+
else
207+
ci['type']
208+
end
209+
if ci['default_value'].nil? && views.include?(table_name)
211210
real_table_name = table_name_or_views_table_name(table_name)
212-
real_column_name = views_real_column_name(table_name,ci[:name])
211+
real_column_name = views_real_column_name(table_name,ci['name'])
213212
col_default_sql = "SELECT c.COLUMN_DEFAULT FROM #{db_name_with_period}INFORMATION_SCHEMA.COLUMNS c WHERE c.TABLE_NAME = '#{real_table_name}' AND c.COLUMN_NAME = '#{real_column_name}'"
214-
ci[:default_value] = info_schema_query { select_value(col_default_sql) }
213+
ci['default_value'] = info_schema_query { select_value(col_default_sql) }
215214
end
216-
ci[:default_value] = case ci[:default_value]
215+
ci['default_value'] = case ci['default_value']
217216
when nil, '(null)', '(NULL)'
218217
nil
219218
else
220-
match_data = ci[:default_value].match(/\A\(+N?'?(.*?)'?\)+\Z/m)
219+
match_data = ci['default_value'].match(/\A\(+N?'?(.*?)'?\)+\Z/m)
221220
match_data ? match_data[1] : nil
222221
end
223-
ci[:null] = ci[:is_nullable].to_i == 1 ; ci.delete(:is_nullable)
222+
ci['null'] = ci['is_nullable'].to_i == 1 ; ci.delete('is_nullable')
224223
ci
225224
end
226225
end

Diff for: lib/active_record/connection_adapters/sqlserver_adapter.rb

+3-3
Original file line numberDiff line numberDiff line change
@@ -97,15 +97,15 @@ def type_cast_code(var_name)
9797
end
9898

9999
def is_identity?
100-
@sqlserver_options[:is_identity]
100+
@sqlserver_options['is_identity']
101101
end
102102

103103
def is_utf8?
104104
sql_type =~ /nvarchar|ntext|nchar/i
105105
end
106106

107107
def table_name
108-
@sqlserver_options[:table_name]
108+
@sqlserver_options['table_name']
109109
end
110110

111111
def table_klass
@@ -118,7 +118,7 @@ def table_klass
118118
end
119119

120120
def database_year
121-
@sqlserver_options[:database_year]
121+
@sqlserver_options['database_year']
122122
end
123123

124124

Diff for: test/cases/execute_procedure_test_sqlserver.rb

-11
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,6 @@ def setup
2020
assert_equal 'TABLE', table_info[:TABLE_TYPE], "Table Info: #{table_info.inspect}"
2121
end
2222

23-
should 'quote bind vars correctly' do
24-
regex = if quote_values_as_utf8?
25-
/EXEC sp_tables N'%sql_server%', NULL, NULL, NULL, 1/
26-
else
27-
/EXEC sp_tables '%sql_server%', NULL, NULL, NULL, 1/
28-
end
29-
assert_sql(regex) do
30-
@klass.execute_procedure :sp_tables, '%sql_server%', nil, nil, nil, true
31-
end
32-
end
33-
3423
should 'allow multiple result sets to be returned' do
3524
results1, results2 = @klass.execute_procedure('sp_helpconstraint','accounts')
3625
assert_instance_of Array, results1

Diff for: test/cases/sqlserver_helper.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,10 @@ def method_added(method)
8080
end
8181

8282
ActiveRecord::ConnectionAdapters::SQLServerAdapter.class_eval do
83-
def raw_select_with_query_record(sql, name = nil)
83+
def raw_select_with_query_record(sql, name=nil, rows_only=false)
8484
$queries_executed ||= []
8585
$queries_executed << sql unless IGNORED_SQL.any? { |r| sql =~ r }
86-
raw_select_without_query_record(sql,name)
86+
raw_select_without_query_record(sql,name,rows_only)
8787
end
8888
alias_method_chain :raw_select, :query_record
8989
end

Diff for: test/profile/connection.rb

-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ def setup
1111
end
1212

1313
def test_select
14-
# Ruby 1.8.7 p302
15-
# 6.180643
1614
ruby_profile :connection_select do
1715
1000.times { @connection.send :select, "SELECT [topics].* FROM [topics]" }
1816
end

0 commit comments

Comments
 (0)