@@ -39,6 +39,8 @@ def cast_result(raw_result)
3939 end
4040
4141 def affected_rows ( raw_result )
42+ return if raw_result . blank?
43+
4244 column_name = lowercase_schema_reflection ? 'affectedrows' : 'AffectedRows'
4345 raw_result . first [ column_name ]
4446 end
@@ -53,18 +55,16 @@ def raw_execute(sql, name = nil, binds = [], prepare: false, async: false, allow
5355 end
5456
5557 def internal_exec_sql_query ( sql , conn )
56- handle = internal_raw_execute ( sql , conn )
58+ handle = raw_connection_run ( sql , conn )
5759 handle_to_names_and_values ( handle , ar_result : true )
58- ensure
59- finish_statement_handle ( handle )
6060 end
6161
6262 def exec_delete ( sql , name = nil , binds = [ ] )
63- super ( sql , name , binds ) || super ( "SELECT @@ROWCOUNT As AffectedRows" , "" , [ ] )
63+ super || super ( "SELECT @@ROWCOUNT As AffectedRows" , "" , [ ] )
6464 end
6565
6666 def exec_update ( sql , name = nil , binds = [ ] )
67- super ( sql , name , binds ) || super ( "SELECT @@ROWCOUNT As AffectedRows" , "" , [ ] )
67+ super || super ( "SELECT @@ROWCOUNT As AffectedRows" , "" , [ ] )
6868 end
6969
7070 def begin_db_transaction
@@ -168,17 +168,8 @@ def execute_procedure(proc_name, *variables)
168168
169169 log ( sql , "Execute Procedure" ) do |notification_payload |
170170 with_raw_connection do |conn |
171- result = internal_raw_execute ( sql , conn )
172- verified!
173- options = { as : :hash , cache_rows : true , timezone : ActiveRecord . default_timezone || :utc }
174-
175- result . each ( options ) do |row |
176- r = row . with_indifferent_access
177- yield ( r ) if block_given?
178- end
179-
180- result = result . each . map { |row | row . is_a? ( Hash ) ? row . with_indifferent_access : row }
181- notification_payload [ :row_count ] = result . count
171+ result = send ( "execute_#{ @config [ :mode ] } _procedure" , sql , conn )
172+ notification_payload [ :row_count ] = result &.count
182173 result
183174 end
184175 end
@@ -312,7 +303,11 @@ def sql_for_insert(sql, pk, binds, returning)
312303 # === SQLServer Specific ======================================== #
313304
314305 def set_identity_insert ( table_name , conn , enable )
315- internal_raw_execute ( "SET IDENTITY_INSERT #{ table_name } #{ enable ? 'ON' : 'OFF' } " , conn , perform_do : true )
306+ if @config [ :mode ] . to_sym == :dblib
307+ internal_raw_execute ( "SET IDENTITY_INSERT #{ table_name } #{ enable ? 'ON' : 'OFF' } " , conn , perform_do : true )
308+ else
309+ internal_raw_execute_odbc ( "SET IDENTITY_INSERT #{ table_name } #{ enable ? 'ON' : 'OFF' } " , conn , perform_do : true )
310+ end
316311 rescue Exception
317312 raise ActiveRecordError , "IDENTITY_INSERT could not be turned #{ enable ? 'ON' : 'OFF' } for table #{ table_name } "
318313 end
@@ -345,7 +340,12 @@ def sp_executesql_sql_type(attr)
345340 value = active_model_attribute? ( attr ) ? attr . value_for_database : attr
346341
347342 if value . is_a? ( Numeric )
348- value > 2_147_483_647 ? "bigint" . freeze : "int" . freeze
343+ if value . is_a? ( Integer )
344+ value > 2_147_483_647 ? "bigint" . freeze : "int" . freeze
345+ else
346+ # For Float, BigDecimal, Rational etc.
347+ value . is_a? ( BigDecimal ) ? "decimal(18,6)" . freeze : "float" . freeze
348+ end
349349 else
350350 "nvarchar(max)" . freeze
351351 end
@@ -427,13 +427,26 @@ def identity_columns(table_name)
427427 # === SQLServer Specific (Selecting) ============================ #
428428
429429 def _raw_select ( sql , conn )
430- handle = internal_raw_execute ( sql , conn )
430+ handle = raw_connection_run ( sql , conn )
431431 handle_to_names_and_values ( handle , fetch : :rows )
432+ end
433+
434+ def raw_connection_run ( sql , conn , perform_do : false )
435+ case @config [ :mode ] . to_sym
436+ when :dblib
437+ internal_raw_execute ( sql , conn , perform_do : perform_do )
438+ when :odbc
439+ internal_raw_execute_odbc ( sql , conn , perform_do : perform_do )
440+ end
441+ end
442+
443+ def handle_to_names_and_values ( handle , options = { } )
444+ send ( "handle_to_names_and_values_#{ @config [ :mode ] } " , handle , options )
432445 ensure
433446 finish_statement_handle ( handle )
434447 end
435448
436- def handle_to_names_and_values ( handle , options = { } )
449+ def handle_to_names_and_values_dblib ( handle , options = { } )
437450 query_options = { } . tap do |qo |
438451 qo [ :timezone ] = ActiveRecord . default_timezone || :utc
439452 qo [ :as ] = ( options [ :ar_result ] || options [ :fetch ] == :rows ) ? :array : :hash
@@ -448,8 +461,31 @@ def handle_to_names_and_values(handle, options = {})
448461 options [ :ar_result ] ? ActiveRecord ::Result . new ( columns , results ) : results
449462 end
450463
464+ def handle_to_names_and_values_odbc ( handle , options = { } )
465+ @raw_connection . use_utc = ActiveRecord . default_timezone || :utc
466+
467+ if options [ :ar_result ]
468+ columns = lowercase_schema_reflection ? handle . columns ( true ) . map { |c | c . name . downcase } : handle . columns ( true ) . map { |c | c . name }
469+ rows = handle . fetch_all || [ ]
470+ ActiveRecord ::Result . new ( columns , rows )
471+ else
472+ case options [ :fetch ]
473+ when :all
474+ handle . each_hash || [ ]
475+ when :rows
476+ handle . fetch_all || [ ]
477+ end
478+ end
479+ end
480+
451481 def finish_statement_handle ( handle )
452- handle . cancel if handle
482+ case @config [ :mode ] . to_sym
483+ when :dblib
484+ handle . cancel if handle
485+ when :odbc
486+ handle . drop if handle && handle . respond_to? ( :drop ) && !handle . finished?
487+ end
488+
453489 handle
454490 end
455491
@@ -462,6 +498,54 @@ def internal_raw_execute(sql, raw_connection, perform_do: false)
462498
463499 perform_do ? result . do : result
464500 end
501+
502+ # Executing SQL for ODBC mode
503+ def internal_raw_execute_odbc ( sql , raw_connection , perform_do : false )
504+ raw_connection . do ( sql ) if perform_do
505+
506+ block_given? ? raw_connection . run_block ( sql ) { |handle | yield ( handle ) } : raw_connection . run ( sql )
507+ end
508+
509+ private
510+
511+ def execute_dblib_procedure ( sql , conn )
512+ result = internal_raw_execute ( sql , conn )
513+ verified!
514+ options = { as : :hash , cache_rows : true , timezone : ActiveRecord . default_timezone || :utc }
515+
516+ raw_rows = result . each ( options ) . map do |row |
517+ row = row . with_indifferent_access
518+ yield ( row ) if block_given?
519+ row
520+ end
521+
522+ raw_rows . map { |row | row . is_a? ( Hash ) ? row . with_indifferent_access : row }
523+ end
524+
525+ def execute_odbc_procedure ( sql , conn )
526+ results = [ ]
527+
528+ internal_raw_execute_odbc ( sql , conn ) do |handle |
529+ get_rows = lambda do
530+ rows = handle_to_names_and_values handle , fetch : :all
531+ rows . each_with_index { |r , i | rows [ i ] = r . with_indifferent_access }
532+ results << rows
533+ end
534+
535+ get_rows . call
536+ get_rows . call while handle_more_results? ( handle )
537+ end
538+
539+ results . many? ? results : results . first
540+ end
541+
542+ def handle_more_results? ( handle )
543+ case @config [ :mode ] . to_sym
544+ when :dblib
545+ when :odbc
546+ handle . more_results
547+ end
548+ end
465549 end
466550 end
467551 end
0 commit comments