@@ -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,18 @@ 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 )
5860 ensure
5961 finish_statement_handle ( handle )
6062 end
6163
6264 def exec_delete ( sql , name = nil , binds = [ ] )
63- super ( sql , name , binds ) || super ( "SELECT @@ROWCOUNT As AffectedRows" , "" , [ ] )
65+ super || super ( "SELECT @@ROWCOUNT As AffectedRows" , "" , [ ] )
6466 end
6567
6668 def exec_update ( sql , name = nil , binds = [ ] )
67- super ( sql , name , binds ) || super ( "SELECT @@ROWCOUNT As AffectedRows" , "" , [ ] )
69+ super || super ( "SELECT @@ROWCOUNT As AffectedRows" , "" , [ ] )
6870 end
6971
7072 def begin_db_transaction
@@ -168,17 +170,8 @@ def execute_procedure(proc_name, *variables)
168170
169171 log ( sql , "Execute Procedure" ) do |notification_payload |
170172 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
173+ result = send ( "execute_#{ @config [ :mode ] } _procedure" , sql , conn )
174+ notification_payload [ :row_count ] = result &.count
182175 result
183176 end
184177 end
@@ -312,7 +305,11 @@ def sql_for_insert(sql, pk, binds, returning)
312305 # === SQLServer Specific ======================================== #
313306
314307 def set_identity_insert ( table_name , conn , enable )
315- internal_raw_execute ( "SET IDENTITY_INSERT #{ table_name } #{ enable ? 'ON' : 'OFF' } " , conn , perform_do : true )
308+ if @config [ :mode ] . to_sym == :dblib
309+ internal_raw_execute ( "SET IDENTITY_INSERT #{ table_name } #{ enable ? 'ON' : 'OFF' } " , conn , perform_do : true )
310+ else
311+ internal_raw_execute_odbc ( "SET IDENTITY_INSERT #{ table_name } #{ enable ? 'ON' : 'OFF' } " , conn , perform_do : true )
312+ end
316313 rescue Exception
317314 raise ActiveRecordError , "IDENTITY_INSERT could not be turned #{ enable ? 'ON' : 'OFF' } for table #{ table_name } "
318315 end
@@ -345,7 +342,12 @@ def sp_executesql_sql_type(attr)
345342 value = active_model_attribute? ( attr ) ? attr . value_for_database : attr
346343
347344 if value . is_a? ( Numeric )
348- value > 2_147_483_647 ? "bigint" . freeze : "int" . freeze
345+ if value . is_a? ( Integer )
346+ value > 2_147_483_647 ? "bigint" . freeze : "int" . freeze
347+ else
348+ # For Float, BigDecimal, Rational etc.
349+ value . is_a? ( BigDecimal ) ? "decimal(18,6)" . freeze : "float" . freeze
350+ end
349351 else
350352 "nvarchar(max)" . freeze
351353 end
@@ -427,13 +429,26 @@ def identity_columns(table_name)
427429 # === SQLServer Specific (Selecting) ============================ #
428430
429431 def _raw_select ( sql , conn )
430- handle = internal_raw_execute ( sql , conn )
432+ handle = raw_connection_run ( sql , conn )
431433 handle_to_names_and_values ( handle , fetch : :rows )
432434 ensure
433435 finish_statement_handle ( handle )
434436 end
435437
438+ def raw_connection_run ( sql , conn , perform_do : false )
439+ case @config [ :mode ] . to_sym
440+ when :dblib
441+ internal_raw_execute ( sql , conn , perform_do : perform_do )
442+ when :odbc
443+ internal_raw_execute_odbc ( sql , conn , perform_do : perform_do )
444+ end
445+ end
446+
436447 def handle_to_names_and_values ( handle , options = { } )
448+ send ( "handle_to_names_and_values_#{ @config [ :mode ] } " , handle , options )
449+ end
450+
451+ def handle_to_names_and_values_dblib ( handle , options = { } )
437452 query_options = { } . tap do |qo |
438453 qo [ :timezone ] = ActiveRecord . default_timezone || :utc
439454 qo [ :as ] = ( options [ :ar_result ] || options [ :fetch ] == :rows ) ? :array : :hash
@@ -448,8 +463,33 @@ def handle_to_names_and_values(handle, options = {})
448463 options [ :ar_result ] ? ActiveRecord ::Result . new ( columns , results ) : results
449464 end
450465
466+ def handle_to_names_and_values_odbc ( handle , options = { } )
467+ @raw_connection . use_utc = ActiveRecord . default_timezone || :utc
468+
469+ if options [ :ar_result ]
470+ columns = lowercase_schema_reflection ? handle . columns ( true ) . map { |c | c . name . downcase } : handle . columns ( true ) . map { |c | c . name }
471+ rows = handle . fetch_all || [ ]
472+ ActiveRecord ::Result . new ( columns , rows )
473+ else
474+ case options [ :fetch ]
475+ when :all
476+ handle . each_hash || [ ]
477+ when :rows
478+ handle . fetch_all || [ ]
479+ end
480+ end
481+ end
482+
451483 def finish_statement_handle ( handle )
452- handle . cancel if handle
484+ return unless handle
485+
486+ case @config [ :mode ] . to_sym
487+ when :dblib
488+ handle . cancel
489+ when :odbc
490+ handle . drop if handle . respond_to? ( :drop ) && !handle . finished?
491+ end
492+
453493 handle
454494 end
455495
@@ -462,6 +502,54 @@ def internal_raw_execute(sql, raw_connection, perform_do: false)
462502
463503 perform_do ? result . do : result
464504 end
505+
506+ # Executing SQL for ODBC mode
507+ def internal_raw_execute_odbc ( sql , raw_connection , perform_do : false )
508+ return raw_connection . do ( sql ) if perform_do
509+
510+ block_given? ? raw_connection . run_block ( sql ) { |handle | yield ( handle ) } : raw_connection . run ( sql )
511+ end
512+
513+ private
514+
515+ def execute_dblib_procedure ( sql , conn )
516+ result = internal_raw_execute ( sql , conn )
517+ verified!
518+ options = { as : :hash , cache_rows : true , timezone : ActiveRecord . default_timezone || :utc }
519+
520+ raw_rows = result . each ( options ) . map do |row |
521+ row = row . with_indifferent_access
522+ yield ( row ) if block_given?
523+ row
524+ end
525+
526+ raw_rows . map { |row | row . is_a? ( Hash ) ? row . with_indifferent_access : row }
527+ end
528+
529+ def execute_odbc_procedure ( sql , conn )
530+ results = [ ]
531+
532+ internal_raw_execute_odbc ( sql , conn ) do |handle |
533+ get_rows = lambda do
534+ rows = handle_to_names_and_values handle , fetch : :all
535+ results << rows . map! ( &:with_indifferent_access )
536+ end
537+
538+ get_rows . call
539+ get_rows . call while handle_more_results? ( handle )
540+ end
541+
542+ results . many? ? results : results . first
543+ end
544+
545+
546+ def handle_more_results? ( handle )
547+ case @config [ :mode ] . to_sym
548+ when :dblib
549+ when :odbc
550+ handle . more_results
551+ end
552+ end
465553 end
466554 end
467555 end
0 commit comments