@@ -39,6 +39,8 @@ def cast_result(raw_result)
3939 end
4040
4141 def affected_rows ( raw_result )
42+ return 0 if raw_result . blank?
43+
4244 column_name = lowercase_schema_reflection ? 'affectedrows' : 'AffectedRows'
4345 raw_result . first [ column_name ]
4446 end
@@ -53,7 +55,7 @@ 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 )
@@ -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
@@ -427,13 +424,26 @@ def identity_columns(table_name)
427424 # === SQLServer Specific (Selecting) ============================ #
428425
429426 def _raw_select ( sql , conn )
430- handle = internal_raw_execute ( sql , conn )
427+ handle = raw_connection_run ( sql , conn )
431428 handle_to_names_and_values ( handle , fetch : :rows )
432429 ensure
433430 finish_statement_handle ( handle )
434431 end
435432
433+ def raw_connection_run ( sql , conn , perform_do : false )
434+ case @config [ :mode ] . to_sym
435+ when :dblib
436+ internal_raw_execute ( sql , conn , perform_do : perform_do )
437+ when :odbc
438+ internal_raw_execute_odbc ( sql , conn , perform_do : perform_do )
439+ end
440+ end
441+
436442 def handle_to_names_and_values ( handle , options = { } )
443+ send ( "handle_to_names_and_values_#{ @config [ :mode ] } " , handle , options )
444+ end
445+
446+ def handle_to_names_and_values_dblib ( handle , options = { } )
437447 query_options = { } . tap do |qo |
438448 qo [ :timezone ] = ActiveRecord . default_timezone || :utc
439449 qo [ :as ] = ( options [ :ar_result ] || options [ :fetch ] == :rows ) ? :array : :hash
@@ -448,8 +458,31 @@ def handle_to_names_and_values(handle, options = {})
448458 options [ :ar_result ] ? ActiveRecord ::Result . new ( columns , results ) : results
449459 end
450460
461+ def handle_to_names_and_values_odbc ( handle , options = { } )
462+ @raw_connection . use_utc = ActiveRecord . default_timezone || :utc
463+
464+ if options [ :ar_result ]
465+ columns = lowercase_schema_reflection ? handle . columns ( true ) . map { |c | c . name . downcase } : handle . columns ( true ) . map { |c | c . name }
466+ rows = handle . fetch_all || [ ]
467+ ActiveRecord ::Result . new ( columns , rows )
468+ else
469+ case options [ :fetch ]
470+ when :all
471+ handle . each_hash || [ ]
472+ when :rows
473+ handle . fetch_all || [ ]
474+ end
475+ end
476+ end
477+
451478 def finish_statement_handle ( handle )
452- handle . cancel if handle
479+ case @config [ :mode ] . to_sym
480+ when :dblib
481+ handle . cancel if handle
482+ when :odbc
483+ handle . drop if handle && handle . respond_to? ( :drop ) && !handle . finished?
484+ end
485+
453486 handle
454487 end
455488
@@ -462,6 +495,55 @@ def internal_raw_execute(sql, raw_connection, perform_do: false)
462495
463496 perform_do ? result . do : result
464497 end
498+
499+ # Executing SQL for ODBC mode
500+ def internal_raw_execute_odbc ( sql , raw_connection , perform_do : false )
501+ raw_connection . do ( sql ) if perform_do
502+
503+ block_given? ? raw_connection . run_block ( sql ) { |handle | yield ( handle ) } : raw_connection . run ( sql )
504+ end
505+
506+ private
507+
508+ def execute_dblib_procedure ( sql , conn )
509+ result = internal_raw_execute ( sql , conn )
510+ verified!
511+ options = { as : :hash , cache_rows : true , timezone : ActiveRecord . default_timezone || :utc }
512+
513+ raw_rows = result . each ( options ) . map do |row |
514+ row = row . with_indifferent_access
515+ yield ( row ) if block_given?
516+ row
517+ end
518+
519+ raw_rows . map { |row | row . is_a? ( Hash ) ? row . with_indifferent_access : row }
520+ end
521+
522+ def execute_odbc_procedure ( sql , conn )
523+ results = [ ]
524+
525+ internal_raw_execute_odbc ( sql , conn ) do |handle |
526+
527+ get_rows = lambda do
528+ rows = handle_to_names_and_values handle , fetch : :all
529+ rows . each_with_index { |r , i | rows [ i ] = r . with_indifferent_access }
530+ results << rows
531+ end
532+
533+ get_rows . call
534+ get_rows . call while handle_more_results? ( handle )
535+ end
536+
537+ results . many? ? results : results . first
538+ end
539+
540+ def handle_more_results? ( handle )
541+ case @config [ :mode ] . to_sym
542+ when :dblib
543+ when :odbc
544+ handle . more_results
545+ end
546+ end
465547 end
466548 end
467549 end
0 commit comments