@@ -60,13 +60,95 @@ class SQLServerAdapter < AbstractAdapter
6060 self . use_output_inserted = true
6161 self . exclude_output_inserted_table_names = Concurrent ::Map . new { false }
6262
63- def initialize ( connection , logger = nil , config = { } )
63+ class << self
64+ def new_client ( config )
65+ case config [ :mode ]
66+ when :dblib
67+ require "tiny_tds"
68+ dblib_connect ( config )
69+ when :odbc
70+ odbc_connect ( config )
71+ else
72+ raise ArgumentError , "Unknown connection mode in #{ config . inspect } ."
73+ end
74+ end
75+
76+ def dblib_connect ( config )
77+ TinyTds ::Client . new (
78+ dataserver : config [ :dataserver ] ,
79+ host : config [ :host ] ,
80+ port : config [ :port ] ,
81+ username : config [ :username ] ,
82+ password : config [ :password ] ,
83+ database : config [ :database ] ,
84+ tds_version : config [ :tds_version ] || "7.3" ,
85+ appname : config_appname ( config ) ,
86+ login_timeout : config_login_timeout ( config ) ,
87+ timeout : config_timeout ( config ) ,
88+ encoding : config_encoding ( config ) ,
89+ azure : config [ :azure ] ,
90+ contained : config [ :contained ]
91+ ) . tap do |client |
92+ if config [ :azure ]
93+ client . execute ( "SET ANSI_NULLS ON" ) . do
94+ client . execute ( "SET ANSI_NULL_DFLT_ON ON" ) . do
95+ client . execute ( "SET ANSI_PADDING ON" ) . do
96+ client . execute ( "SET ANSI_WARNINGS ON" ) . do
97+ else
98+ client . execute ( "SET ANSI_DEFAULTS ON" ) . do
99+ end
100+ client . execute ( "SET QUOTED_IDENTIFIER ON" ) . do
101+ client . execute ( "SET CURSOR_CLOSE_ON_COMMIT OFF" ) . do
102+ client . execute ( "SET IMPLICIT_TRANSACTIONS OFF" ) . do
103+ client . execute ( "SET TEXTSIZE 2147483647" ) . do
104+ client . execute ( "SET CONCAT_NULL_YIELDS_NULL ON" ) . do
105+ end
106+ rescue TinyTds ::Error => e
107+ raise ActiveRecord ::NoDatabaseError if e . message . match ( /database .* does not exist/i )
108+ raise e
109+ end
110+
111+
112+ def odbc_connect ( config )
113+ if config [ :dsn ] . include? ( ';' )
114+ driver = ODBC ::Driver . new . tap do |d |
115+ d . name = config [ :dsn_name ] || 'Driver1'
116+ d . attrs = config [ :dsn ] . split ( ';' ) . map { |atr | atr . split ( '=' ) } . reject { |kv | kv . size != 2 } . reduce ( { } ) { |a , e | k , v = e ; a [ k ] = v ; a }
117+ end
118+ ODBC ::Database . new . drvconnect ( driver )
119+ else
120+ ODBC . connect config [ :dsn ] , config [ :username ] , config [ :password ]
121+ end . tap do |c |
122+ begin
123+ c . use_time = true
124+ c . use_utc = ActiveRecord . default_timezone == :utc
125+ rescue Exception
126+ warn 'Ruby ODBC v0.99992 or higher is required.'
127+ end
128+ end
129+ end
130+
131+ def config_appname ( config )
132+ config [ :appname ] || configure_application_name || Rails . application . class . name . split ( "::" ) . first rescue nil
133+ end
134+
135+ def config_login_timeout ( config )
136+ config [ :login_timeout ] . present? ? config [ :login_timeout ] . to_i : nil
137+ end
138+
139+ def config_timeout ( config )
140+ config [ :timeout ] . present? ? config [ :timeout ] . to_i / 1000 : nil
141+ end
142+
143+ def config_encoding ( config )
144+ config [ :encoding ] . present? ? config [ :encoding ] : nil
145+ end
146+ end
147+
148+ def initialize ( connection , logger , _connection_options , config )
64149 super ( connection , logger , config )
65- # Our Responsibility
66150 @connection_options = config
67- connect
68- initialize_dateformatter
69- use_database
151+ configure_connection
70152 end
71153
72154 # === Abstract Adapter ========================================== #
@@ -229,6 +311,14 @@ def reset!
229311 do_execute "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION"
230312 end
231313
314+ def configure_connection
315+ @spid = _raw_select ( "SELECT @@SPID" , fetch : :rows ) . first . first
316+ @version_year = version_year
317+
318+ initialize_dateformatter
319+ use_database
320+ end
321+
232322 # === Abstract Adapter (Misc Support) =========================== #
233323
234324 def tables_with_referential_integrity
@@ -411,100 +501,19 @@ def translate_exception(e, message:, sql:, binds:)
411501
412502 # === SQLServer Specific (Connection Management) ================ #
413503
414- def connect
415- config = @connection_options
416- @connection = case config [ :mode ]
417- when :dblib
418- dblib_connect ( config )
419- when :odbc
420- odbc_connect ( config )
421- end
422- @spid = _raw_select ( "SELECT @@SPID" , fetch : :rows ) . first . first
423- @version_year = version_year
424- configure_connection
425- end
426-
427504 def connection_errors
428505 @connection_errors ||= [ ] . tap do |errors |
429506 errors << TinyTds ::Error if defined? ( TinyTds ::Error )
430507 errors << ODBC ::Error if defined? ( ODBC ::Error )
431508 end
432509 end
433510
434- def dblib_connect ( config )
435- TinyTds ::Client . new (
436- dataserver : config [ :dataserver ] ,
437- host : config [ :host ] ,
438- port : config [ :port ] ,
439- username : config [ :username ] ,
440- password : config [ :password ] ,
441- database : config [ :database ] ,
442- tds_version : config [ :tds_version ] || "7.3" ,
443- appname : config_appname ( config ) ,
444- login_timeout : config_login_timeout ( config ) ,
445- timeout : config_timeout ( config ) ,
446- encoding : config_encoding ( config ) ,
447- azure : config [ :azure ] ,
448- contained : config [ :contained ]
449- ) . tap do |client |
450- if config [ :azure ]
451- client . execute ( "SET ANSI_NULLS ON" ) . do
452- client . execute ( "SET ANSI_NULL_DFLT_ON ON" ) . do
453- client . execute ( "SET ANSI_PADDING ON" ) . do
454- client . execute ( "SET ANSI_WARNINGS ON" ) . do
455- else
456- client . execute ( "SET ANSI_DEFAULTS ON" ) . do
457- end
458- client . execute ( "SET QUOTED_IDENTIFIER ON" ) . do
459- client . execute ( "SET CURSOR_CLOSE_ON_COMMIT OFF" ) . do
460- client . execute ( "SET IMPLICIT_TRANSACTIONS OFF" ) . do
461- client . execute ( "SET TEXTSIZE 2147483647" ) . do
462- client . execute ( "SET CONCAT_NULL_YIELDS_NULL ON" ) . do
463- end
464- end
465-
466- def odbc_connect ( config )
467- if config [ :dsn ] . include? ( ';' )
468- driver = ODBC ::Driver . new . tap do |d |
469- d . name = config [ :dsn_name ] || 'Driver1'
470- d . attrs = config [ :dsn ] . split ( ';' ) . map { |atr | atr . split ( '=' ) } . reject { |kv | kv . size != 2 } . reduce ( { } ) { |a , e | k , v = e ; a [ k ] = v ; a }
471- end
472- ODBC ::Database . new . drvconnect ( driver )
473- else
474- ODBC . connect config [ :dsn ] , config [ :username ] , config [ :password ]
475- end . tap do |c |
476- begin
477- c . use_time = true
478- c . use_utc = ActiveRecord ::Base . default_timezone == :utc
479- rescue Exception
480- warn 'Ruby ODBC v0.99992 or higher is required.'
481- end
482- end
483- end
484-
485- def config_appname ( config )
486- config [ :appname ] || configure_application_name || Rails . application . class . name . split ( "::" ) . first rescue nil
487- end
488-
489- def config_login_timeout ( config )
490- config [ :login_timeout ] . present? ? config [ :login_timeout ] . to_i : nil
491- end
492-
493- def config_timeout ( config )
494- config [ :timeout ] . present? ? config [ :timeout ] . to_i / 1000 : nil
495- end
496-
497- def config_encoding ( config )
498- config [ :encoding ] . present? ? config [ :encoding ] : nil
499- end
500-
501- def configure_connection ; end
502-
503511 def configure_application_name ; end
504512
505513 def initialize_dateformatter
506514 @database_dateformat = user_options_dateformat
507515 a , b , c = @database_dateformat . each_char . to_a
516+
508517 [ a , b , c ] . each { |f | f . upcase! if f == "y" }
509518 dateformat = "%#{ a } -%#{ b } -%#{ c } "
510519 ::Date ::DATE_FORMATS [ :_sqlserver_dateformat ] = dateformat
@@ -527,6 +536,13 @@ def version_year
527536 def sqlserver_version
528537 @sqlserver_version ||= _raw_select ( "SELECT @@version" , fetch : :rows ) . first . first . to_s
529538 end
539+
540+ private
541+
542+ def connect
543+ @connection = self . class . new_client ( @connection_options )
544+ configure_connection
545+ end
530546 end
531547 end
532548end
0 commit comments