@@ -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
@@ -342,10 +339,16 @@ def sp_executesql_sql_type(attr)
342339 end
343340 end
344341
342+
345343 value = active_model_attribute? ( attr ) ? attr . value_for_database : attr
346344
347345 if value . is_a? ( Numeric )
348- value > 2_147_483_647 ? "bigint" . freeze : "int" . freeze
346+ if value . is_a? ( Integer )
347+ value > 2_147_483_647 ? "bigint" . freeze : "int" . freeze
348+ else
349+ # For Float, BigDecimal, Rational etc.
350+ value . is_a? ( BigDecimal ) ? "decimal(18,6)" . freeze : "float" . freeze
351+ end
349352 else
350353 "nvarchar(max)" . freeze
351354 end
@@ -427,13 +430,26 @@ def identity_columns(table_name)
427430 # === SQLServer Specific (Selecting) ============================ #
428431
429432 def _raw_select ( sql , conn )
430- handle = internal_raw_execute ( sql , conn )
433+ handle = raw_connection_run ( sql , conn )
431434 handle_to_names_and_values ( handle , fetch : :rows )
432435 ensure
433436 finish_statement_handle ( handle )
434437 end
435438
439+ def raw_connection_run ( sql , conn , perform_do : false )
440+ case @config [ :mode ] . to_sym
441+ when :dblib
442+ internal_raw_execute ( sql , conn , perform_do : perform_do )
443+ when :odbc
444+ internal_raw_execute_odbc ( sql , conn , perform_do : perform_do )
445+ end
446+ end
447+
436448 def handle_to_names_and_values ( handle , options = { } )
449+ send ( "handle_to_names_and_values_#{ @config [ :mode ] } " , handle , options )
450+ end
451+
452+ def handle_to_names_and_values_dblib ( handle , options = { } )
437453 query_options = { } . tap do |qo |
438454 qo [ :timezone ] = ActiveRecord . default_timezone || :utc
439455 qo [ :as ] = ( options [ :ar_result ] || options [ :fetch ] == :rows ) ? :array : :hash
@@ -448,8 +464,32 @@ def handle_to_names_and_values(handle, options = {})
448464 options [ :ar_result ] ? ActiveRecord ::Result . new ( columns , results ) : results
449465 end
450466
467+ def handle_to_names_and_values_odbc ( handle , options = { } )
468+ @raw_connection . use_utc = ActiveRecord . default_timezone || :utc
469+
470+
471+ if options [ :ar_result ]
472+ columns = lowercase_schema_reflection ? handle . columns ( true ) . map { |c | c . name . downcase } : handle . columns ( true ) . map { |c | c . name }
473+ p rows = handle . fetch_all || [ ]
474+ ActiveRecord ::Result . new ( columns , rows )
475+ else
476+ case options [ :fetch ]
477+ when :all
478+ handle . each_hash || [ ]
479+ when :rows
480+ handle . fetch_all || [ ]
481+ end
482+ end
483+ end
484+
451485 def finish_statement_handle ( handle )
452- handle . cancel if handle
486+ case @config [ :mode ] . to_sym
487+ when :dblib
488+ handle . cancel if handle
489+ when :odbc
490+ handle . drop if handle && handle . respond_to? ( :drop ) && !handle . finished?
491+ end
492+
453493 handle
454494 end
455495
@@ -462,6 +502,59 @@ 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+ raw_connection . do ( sql ) if perform_do
509+
510+
511+ block_given? ? raw_connection . run_block ( sql ) { |handle | yield ( handle ) } : raw_connection . run ( sql )
512+ end
513+
514+ private
515+
516+ def execute_dblib_procedure ( sql , conn )
517+ result = internal_raw_execute ( sql , conn )
518+ verified!
519+ options = { as : :hash , cache_rows : true , timezone : ActiveRecord . default_timezone || :utc }
520+
521+ raw_rows = result . each ( options ) . map do |row |
522+ row = row . with_indifferent_access
523+ yield ( row ) if block_given?
524+ row
525+ end
526+
527+ raw_rows . map { |row | row . is_a? ( Hash ) ? row . with_indifferent_access : row }
528+ end
529+
530+ def execute_odbc_procedure ( sql , conn )
531+ results = [ ]
532+
533+ internal_raw_execute_odbc ( sql , conn ) do |handle |
534+
535+ get_rows = lambda do
536+ rows = handle_to_names_and_values handle , fetch : :all
537+ rows . each_with_index { |r , i | rows [ i ] = r . with_indifferent_access }
538+ results << rows
539+ end
540+
541+ get_rows . call
542+
543+ byebug
544+
545+ get_rows . call while handle_more_results? ( handle )
546+ end
547+
548+ results . many? ? results : results . first
549+ end
550+
551+ def handle_more_results? ( handle )
552+ case @config [ :mode ] . to_sym
553+ when :dblib
554+ when :odbc
555+ handle . more_results
556+ end
557+ end
465558 end
466559 end
467560 end
0 commit comments