activerecord-sqlserver-adapter 3.2.18 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +4 -28
  3. data/VERSION +1 -1
  4. data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +2 -7
  5. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +6 -9
  6. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +3 -25
  7. data/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb +4 -14
  8. data/lib/active_record/connection_adapters/sqlserver/core_ext/relation.rb +1 -3
  9. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +2 -4
  10. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +74 -80
  11. data/lib/active_record/connection_adapters/sqlserver/errors.rb +10 -14
  12. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +24 -15
  13. data/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +24 -19
  14. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +28 -0
  15. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +118 -77
  16. data/lib/active_record/connection_adapters/sqlserver/showplan.rb +10 -13
  17. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +8 -11
  18. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +2 -5
  19. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +23 -0
  20. data/lib/active_record/connection_adapters/sqlserver/utils.rb +4 -10
  21. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +121 -247
  22. data/lib/active_record/connection_adapters/sqlserver_column.rb +116 -0
  23. data/lib/active_record/sqlserver_base.rb +28 -0
  24. data/lib/active_record/sqlserver_test_case.rb +17 -0
  25. data/lib/arel/arel_sqlserver.rb +5 -0
  26. data/lib/arel/nodes_sqlserver.rb +14 -0
  27. data/lib/arel/select_manager_sqlserver.rb +62 -0
  28. data/lib/arel/visitors/sqlserver.rb +251 -188
  29. metadata +32 -10
  30. data/lib/active_record/connection_adapters/sqlserver/core_ext/database_statements.rb +0 -97
@@ -5,54 +5,52 @@ module ActiveRecord
5
5
  module ConnectionAdapters
6
6
  module Sqlserver
7
7
  module Showplan
8
-
9
8
  OPTION_ALL = 'SHOWPLAN_ALL'
10
9
  OPTION_TEXT = 'SHOWPLAN_TEXT'
11
10
  OPTION_XML = 'SHOWPLAN_XML'
12
11
  OPTIONS = [OPTION_ALL, OPTION_TEXT, OPTION_XML]
13
-
12
+
14
13
  def explain(arel, binds = [])
15
14
  sql = to_sql(arel)
16
15
  result = with_showplan_on { do_exec_query(sql, 'EXPLAIN', binds) }
17
16
  printer = showplan_printer.new(result)
18
17
  printer.pp
19
18
  end
20
-
21
-
19
+
22
20
  protected
23
-
21
+
24
22
  def with_showplan_on
25
23
  set_showplan_option(true)
26
24
  yield
27
25
  ensure
28
26
  set_showplan_option(false)
29
27
  end
30
-
28
+
31
29
  def set_showplan_option(enable = true)
32
30
  sql = "SET #{option} #{enable ? 'ON' : 'OFF'}"
33
31
  raw_connection_do(sql)
34
- rescue Exception => e
32
+ rescue Exception
35
33
  raise ActiveRecordError, "#{option} could not be turned #{enable ? 'ON' : 'OFF'}, perhaps you do not have SHOWPLAN permissions?"
36
34
  end
37
-
35
+
38
36
  def option
39
37
  (SQLServerAdapter.showplan_option || OPTION_ALL).tap do |opt|
40
38
  raise(ArgumentError, "Unknown SHOWPLAN option #{opt.inspect} found.") if OPTIONS.exclude?(opt)
41
39
  end
42
40
  end
43
-
41
+
44
42
  def showplan_all?
45
43
  option == OPTION_ALL
46
44
  end
47
-
45
+
48
46
  def showplan_text?
49
47
  option == OPTION_TEXT
50
48
  end
51
-
49
+
52
50
  def showplan_xml?
53
51
  option == OPTION_XML
54
52
  end
55
-
53
+
56
54
  def showplan_printer
57
55
  case option
58
56
  when OPTION_XML then PrinterXml
@@ -60,7 +58,6 @@ module ActiveRecord
60
58
  else PrinterTable
61
59
  end
62
60
  end
63
-
64
61
  end
65
62
  end
66
63
  end
@@ -3,17 +3,16 @@ module ActiveRecord
3
3
  module Sqlserver
4
4
  module Showplan
5
5
  class PrinterTable
6
-
7
6
  cattr_accessor :max_column_width, :cell_padding
8
7
  self.max_column_width = 50
9
8
  self.cell_padding = 1
10
-
9
+
11
10
  attr_reader :result
12
-
11
+
13
12
  def initialize(result)
14
13
  @result = result
15
14
  end
16
-
15
+
17
16
  def pp
18
17
  @widths = compute_column_widths
19
18
  @separator = build_separator
@@ -27,7 +26,7 @@ module ActiveRecord
27
26
  pp << @separator
28
27
  pp.join("\n") + "\n"
29
28
  end
30
-
29
+
31
30
  private
32
31
 
33
32
  def compute_column_widths
@@ -40,11 +39,11 @@ module ActiveRecord
40
39
  end
41
40
  end
42
41
  end
43
-
42
+
44
43
  def build_separator
45
- '+' + @widths.map {|w| '-' * (w + (cell_padding*2))}.join('+') + '+'
44
+ '+' + @widths.map { |w| '-' * (w + (cell_padding * 2)) }.join('+') + '+'
46
45
  end
47
-
46
+
48
47
  def build_cells(items)
49
48
  cells = []
50
49
  items.each_with_index do |item, i|
@@ -52,7 +51,7 @@ module ActiveRecord
52
51
  end
53
52
  "| #{cells.join(' | ')} |"
54
53
  end
55
-
54
+
56
55
  def cast_item(item)
57
56
  case item
58
57
  when NilClass then 'NULL'
@@ -60,9 +59,7 @@ module ActiveRecord
60
59
  else item.to_s.truncate(max_column_width)
61
60
  end
62
61
  end
63
-
64
62
  end
65
-
66
63
  end
67
64
  end
68
65
  end
@@ -3,22 +3,19 @@ module ActiveRecord
3
3
  module Sqlserver
4
4
  module Showplan
5
5
  class PrinterXml
6
-
7
6
  def initialize(result)
8
7
  @result = result
9
8
  end
10
-
9
+
11
10
  def pp
12
11
  xml = @result.rows.first.first
13
12
  if defined?(Nokogiri)
14
- Nokogiri::XML(xml).to_xml :indent => 2, :encoding => 'UTF-8'
13
+ Nokogiri::XML(xml).to_xml indent: 2, encoding: 'UTF-8'
15
14
  else
16
15
  xml
17
16
  end
18
17
  end
19
-
20
18
  end
21
-
22
19
  end
23
20
  end
24
21
  end
@@ -0,0 +1,23 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module Sqlserver
4
+ class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
5
+ def uuid(name, options = {})
6
+ column(name, 'uniqueidentifier', options)
7
+ end
8
+
9
+ def primary_key(name, type = :primary_key, options = {})
10
+ return super unless type == :uuid
11
+ options[:default] = options.fetch(:default, 'NEWID()')
12
+ options[:primary_key] = true
13
+ column name, type, options
14
+ end
15
+
16
+ def column(name, type = nil, options = {})
17
+ super
18
+ self
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -2,31 +2,25 @@ module ActiveRecord
2
2
  module ConnectionAdapters
3
3
  module Sqlserver
4
4
  class Utils
5
-
6
5
  class << self
7
-
8
6
  def unquote_string(string)
9
7
  string.to_s.gsub(/\'\'/, "'")
10
8
  end
11
-
9
+
12
10
  def unqualify_table_name(table_name)
13
- table_name.to_s.split('.').last.tr('[]','')
11
+ table_name.to_s.split('.').last.tr('[]', '')
14
12
  end
15
13
 
16
14
  def unqualify_table_schema(table_name)
17
- table_name.to_s.split('.')[-2].gsub(/[\[\]]/,'') rescue nil
15
+ table_name.to_s.split('.')[-2].gsub(/[\[\]]/, '') rescue nil
18
16
  end
19
17
 
20
18
  def unqualify_db_name(table_name)
21
19
  table_names = table_name.to_s.split('.')
22
- table_names.length == 3 ? table_names.first.tr('[]','') : nil
20
+ table_names.length == 3 ? table_names.first.tr('[]', '') : nil
23
21
  end
24
-
25
22
  end
26
-
27
23
  end
28
24
  end
29
25
  end
30
26
  end
31
-
32
-
@@ -1,12 +1,12 @@
1
1
  require 'base64'
2
- require 'arel/visitors/sqlserver'
2
+ require 'arel/arel_sqlserver'
3
+ require 'arel/visitors/bind_visitor'
3
4
  require 'active_record'
4
5
  require 'active_record/base'
5
6
  require 'active_support/concern'
6
7
  require 'active_support/core_ext/string'
7
8
  require 'active_record/connection_adapters/abstract_adapter'
8
9
  require 'active_record/connection_adapters/sqlserver/core_ext/active_record'
9
- require 'active_record/connection_adapters/sqlserver/core_ext/database_statements'
10
10
  require 'active_record/connection_adapters/sqlserver/core_ext/explain'
11
11
  require 'active_record/connection_adapters/sqlserver/core_ext/explain_subscriber'
12
12
  require 'active_record/connection_adapters/sqlserver/core_ext/relation'
@@ -14,163 +14,19 @@ require 'active_record/connection_adapters/sqlserver/database_limits'
14
14
  require 'active_record/connection_adapters/sqlserver/database_statements'
15
15
  require 'active_record/connection_adapters/sqlserver/errors'
16
16
  require 'active_record/connection_adapters/sqlserver/schema_cache'
17
+ require 'active_record/connection_adapters/sqlserver/schema_creation'
17
18
  require 'active_record/connection_adapters/sqlserver/schema_statements'
18
19
  require 'active_record/connection_adapters/sqlserver/showplan'
20
+ require 'active_record/connection_adapters/sqlserver/table_definition'
19
21
  require 'active_record/connection_adapters/sqlserver/quoting'
20
22
  require 'active_record/connection_adapters/sqlserver/utils'
21
23
 
22
- module ActiveRecord
23
-
24
- class Base
25
-
26
- def self.sqlserver_connection(config) #:nodoc:
27
- config = config.symbolize_keys
28
- config.reverse_merge! :mode => :dblib
29
- mode = config[:mode].to_s.downcase.underscore.to_sym
30
- case mode
31
- when :dblib
32
- require 'tiny_tds'
33
- when :odbc
34
- raise ArgumentError, 'Missing :dsn configuration.' unless config.has_key?(:dsn)
35
- require 'odbc'
36
- require 'active_record/connection_adapters/sqlserver/core_ext/odbc'
37
- else
38
- raise ArgumentError, "Unknown connection mode in #{config.inspect}."
39
- end
40
- ConnectionAdapters::SQLServerAdapter.new(nil, logger, nil, config.merge(:mode=>mode))
41
- end
42
-
43
- protected
44
-
45
- def self.did_retry_sqlserver_connection(connection,count)
46
- logger.info "CONNECTION RETRY: #{connection.class.name} retry ##{count}."
47
- end
48
-
49
- def self.did_lose_sqlserver_connection(connection)
50
- logger.info "CONNECTION LOST: #{connection.class.name}"
51
- end
52
-
53
- end
24
+ require 'active_record/sqlserver_base'
25
+ require 'active_record/connection_adapters/sqlserver_column'
54
26
 
27
+ module ActiveRecord
55
28
  module ConnectionAdapters
56
-
57
- class SQLServerColumn < Column
58
-
59
- def initialize(name, default, sql_type = nil, null = true, sqlserver_options = {})
60
- @sqlserver_options = sqlserver_options.symbolize_keys
61
- super(name, default, sql_type, null)
62
- @primary = @sqlserver_options[:is_identity] || @sqlserver_options[:is_primary]
63
- end
64
-
65
- class << self
66
-
67
- def string_to_binary(value)
68
- "0x#{value.unpack("H*")[0]}"
69
- end
70
-
71
- def binary_to_string(value)
72
- value =~ /[^[:xdigit:]]/ ? value : [value].pack('H*')
73
- end
74
-
75
- end
76
-
77
- def is_identity?
78
- @sqlserver_options[:is_identity]
79
- end
80
-
81
- def is_primary?
82
- @sqlserver_options[:is_primary]
83
- end
84
-
85
- def is_utf8?
86
- !!(@sql_type =~ /nvarchar|ntext|nchar/i)
87
- end
88
-
89
- def is_integer?
90
- !!(@sql_type =~ /int/i)
91
- end
92
-
93
- def is_real?
94
- !!(@sql_type =~ /real/i)
95
- end
96
-
97
- def sql_type_for_statement
98
- if is_integer? || is_real?
99
- sql_type.sub(/\((\d+)?\)/,'')
100
- else
101
- sql_type
102
- end
103
- end
104
-
105
- def default_function
106
- @sqlserver_options[:default_function]
107
- end
108
-
109
- def table_name
110
- @sqlserver_options[:table_name]
111
- end
112
-
113
- def table_klass
114
- @table_klass ||= begin
115
- table_name.classify.constantize
116
- rescue StandardError, NameError, LoadError
117
- nil
118
- end
119
- (@table_klass && @table_klass < ActiveRecord::Base) ? @table_klass : nil
120
- end
121
-
122
- def database_year
123
- @sqlserver_options[:database_year]
124
- end
125
-
126
-
127
- private
128
-
129
- def extract_limit(sql_type)
130
- case sql_type
131
- when /^smallint/i
132
- 2
133
- when /^int/i
134
- 4
135
- when /^bigint/i
136
- 8
137
- when /\(max\)/, /decimal/, /numeric/
138
- nil
139
- else
140
- super
141
- end
142
- end
143
-
144
- def simplified_type(field_type)
145
- case field_type
146
- when /real/i then :float
147
- when /money/i then :decimal
148
- when /image/i then :binary
149
- when /bit/i then :boolean
150
- when /uniqueidentifier/i then :string
151
- when /datetime/i then simplified_datetime
152
- when /varchar\(max\)/ then :text
153
- when /timestamp/ then :binary
154
- else super
155
- end
156
- end
157
-
158
- def simplified_datetime
159
- if database_year >= 2008
160
- :datetime
161
- elsif table_klass && table_klass.coerced_sqlserver_date_columns.include?(name)
162
- :date
163
- elsif table_klass && table_klass.coerced_sqlserver_time_columns.include?(name)
164
- :time
165
- else
166
- :datetime
167
- end
168
- end
169
-
170
- end #class SQLServerColumn
171
-
172
29
  class SQLServerAdapter < AbstractAdapter
173
-
174
30
  include Sqlserver::Quoting
175
31
  include Sqlserver::DatabaseStatements
176
32
  include Sqlserver::Showplan
@@ -178,20 +34,22 @@ module ActiveRecord
178
34
  include Sqlserver::DatabaseLimits
179
35
  include Sqlserver::Errors
180
36
 
181
- VERSION = File.read(File.expand_path("../../../../VERSION",__FILE__)).strip
37
+ VERSION = File.read(File.expand_path('../../../../VERSION', __FILE__)).strip
182
38
  ADAPTER_NAME = 'SQLServer'.freeze
183
39
  DATABASE_VERSION_REGEXP = /Microsoft SQL Server\s+"?(\d{4}|\w+)"?/
184
- SUPPORTED_VERSIONS = [2005,2008,2010,2011,2012,2014,2016,2017]
40
+ SUPPORTED_VERSIONS = [2005, 2008, 2010, 2011, 2012]
185
41
 
186
42
  attr_reader :database_version, :database_year, :spid, :product_level, :product_version, :edition
187
43
 
188
44
  cattr_accessor :native_text_database_type, :native_binary_database_type, :native_string_database_type,
189
- :enable_default_unicode_types, :auto_connect, :retry_deadlock_victim,
190
- :cs_equality_operator, :lowercase_schema_reflection, :auto_connect_duration,
191
- :showplan_option
45
+ :enable_default_unicode_types, :auto_connect, :cs_equality_operator,
46
+ :lowercase_schema_reflection, :auto_connect_duration, :showplan_option
192
47
 
193
48
  self.enable_default_unicode_types = true
194
49
 
50
+ class BindSubstitution < Arel::Visitors::SQLServer # :nodoc:
51
+ include Arel::Visitors::BindVisitor
52
+ end
195
53
 
196
54
  def initialize(connection, logger, pool, config)
197
55
  super(connection, logger, pool)
@@ -207,12 +65,10 @@ module ActiveRecord
207
65
  if @database_version =~ /Azure/i
208
66
  @sqlserver_azure = true
209
67
  @database_version.match(/\s-\s([0-9.]+)/)[1]
210
- year = 2016
211
- elsif @database_version =~ /vNext/i
212
- year = 2016
68
+ year = 2012
213
69
  else
214
70
  year = DATABASE_VERSION_REGEXP.match(@database_version)[1]
215
- year == "Denali" ? 2011 : year.to_i
71
+ year == 'Denali' ? 2011 : year.to_i
216
72
  end
217
73
  rescue
218
74
  0
@@ -222,7 +78,7 @@ module ActiveRecord
222
78
  @edition = select_value "SELECT CAST(SERVERPROPERTY('edition') AS VARCHAR(128))", 'SCHEMA'
223
79
  initialize_dateformatter
224
80
  use_database
225
- unless (@sqlserver_azure == true || SUPPORTED_VERSIONS.include?(@database_year))
81
+ unless @sqlserver_azure == true || SUPPORTED_VERSIONS.include?(@database_year)
226
82
  raise NotImplementedError, "Currently, only #{SUPPORTED_VERSIONS.to_sentence} are supported. We got back #{@database_version}."
227
83
  end
228
84
  end
@@ -261,6 +117,10 @@ module ActiveRecord
261
117
  true
262
118
  end
263
119
 
120
+ def supports_partial_index?
121
+ @database_year >= 2008
122
+ end
123
+
264
124
  def supports_explain?
265
125
  true
266
126
  end
@@ -279,19 +139,21 @@ module ActiveRecord
279
139
  when :dblib
280
140
  return @connection.active?
281
141
  end
282
- raw_connection_do("SELECT 1")
142
+ raw_connection_do('SELECT 1')
283
143
  true
284
144
  rescue *lost_connection_exceptions
285
145
  false
286
146
  end
287
147
 
288
148
  def reconnect!
149
+ reset_transaction
289
150
  disconnect!
290
151
  connect
291
152
  active?
292
153
  end
293
154
 
294
155
  def disconnect!
156
+ reset_transaction
295
157
  @spid = nil
296
158
  case @connection_options[:mode]
297
159
  when :dblib
@@ -302,18 +164,22 @@ module ActiveRecord
302
164
  end
303
165
 
304
166
  def reset!
305
- remove_database_connections_and_rollback { }
167
+ remove_database_connections_and_rollback {}
306
168
  end
307
169
 
308
170
  # === Abstract Adapter (Misc Support) =========================== #
309
171
 
310
172
  def pk_and_sequence_for(table_name)
311
- idcol = identity_column(table_name)
312
- idcol ? [idcol.name,nil] : nil
173
+ pk = primary_key(table_name)
174
+ pk ? [pk, nil] : nil
313
175
  end
314
176
 
315
177
  def primary_key(table_name)
316
- identity_column(table_name).try(:name) || schema_cache.columns[table_name].detect(&:is_primary?).try(:name)
178
+ identity_column(table_name).try(:name) || schema_cache.columns(table_name).find(&:is_primary?).try(:name)
179
+ end
180
+
181
+ def schema_creation
182
+ Sqlserver::SchemaCreation.new self
317
183
  end
318
184
 
319
185
  # === SQLServer Specific (DB Reflection) ======================== #
@@ -358,11 +224,6 @@ module ActiveRecord
358
224
  @@auto_connect_duration ||= 10
359
225
  end
360
226
 
361
- def retry_deadlock_victim
362
- @@retry_deadlock_victim.is_a?(FalseClass) ? false : true
363
- end
364
- alias :retry_deadlock_victim? :retry_deadlock_victim
365
-
366
227
  def native_string_database_type
367
228
  @@native_string_database_type || (enable_default_unicode_types ? 'nvarchar' : 'varchar')
368
229
  end
@@ -394,13 +255,13 @@ module ActiveRecord
394
255
  def translate_exception(e, message)
395
256
  case message
396
257
  when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i
397
- RecordNotUnique.new(message,e)
258
+ RecordNotUnique.new(message, e)
398
259
  when /conflicted with the foreign key constraint/i
399
- InvalidForeignKey.new(message,e)
260
+ InvalidForeignKey.new(message, e)
400
261
  when /has been chosen as the deadlock victim/i
401
- DeadlockVictim.new(message,e)
262
+ DeadlockVictim.new(message, e)
402
263
  when *lost_connection_messages
403
- LostConnection.new(message,e)
264
+ LostConnection.new(message, e)
404
265
  else
405
266
  super
406
267
  end
@@ -412,64 +273,84 @@ module ActiveRecord
412
273
  config = @connection_options
413
274
  @connection = case config[:mode]
414
275
  when :dblib
415
- appname = config[:appname] || configure_application_name || Rails.application.class.name.split('::').first rescue nil
416
- login_timeout = config[:login_timeout].present? ? config[:login_timeout].to_i : nil
417
- timeout = config[:timeout].present? ? config[:timeout].to_i/1000 : nil
418
- encoding = config[:encoding].present? ? config[:encoding] : nil
419
- TinyTds::Client.new({
420
- :dataserver => config[:dataserver],
421
- :host => config[:host],
422
- :port => config[:port],
423
- :username => config[:username],
424
- :password => config[:password],
425
- :database => config[:database],
426
- :tds_version => config[:tds_version],
427
- :appname => appname,
428
- :login_timeout => login_timeout,
429
- :timeout => timeout,
430
- :encoding => encoding,
431
- :azure => config[:azure]
432
- }).tap do |client|
433
- if config[:azure]
434
- client.execute("SET ANSI_NULLS ON").do
435
- client.execute("SET CURSOR_CLOSE_ON_COMMIT OFF").do
436
- client.execute("SET ANSI_NULL_DFLT_ON ON").do
437
- client.execute("SET IMPLICIT_TRANSACTIONS OFF").do
438
- client.execute("SET ANSI_PADDING ON").do
439
- client.execute("SET QUOTED_IDENTIFIER ON")
440
- client.execute("SET ANSI_WARNINGS ON").do
441
- else
442
- client.execute("SET ANSI_DEFAULTS ON").do
443
- client.execute("SET CURSOR_CLOSE_ON_COMMIT OFF").do
444
- client.execute("SET IMPLICIT_TRANSACTIONS OFF").do
445
- end
446
- client.execute("SET TEXTSIZE 2147483647").do
447
- client.execute("SET CONCAT_NULL_YIELDS_NULL ON").do
448
- end
276
+ dblib_connect(config)
449
277
  when :odbc
450
- if config[:dsn].include?(';')
451
- driver = ODBC::Driver.new.tap do |d|
452
- d.name = config[:dsn_name] || 'Driver1'
453
- d.attrs = config[:dsn].split(';').map{ |atr| atr.split('=') }.reject{ |kv| kv.size != 2 }.inject({}){ |h,kv| k,v = kv ; h[k] = v ; h }
454
- end
455
- ODBC::Database.new.drvconnect(driver)
456
- else
457
- ODBC.connect config[:dsn], config[:username], config[:password]
458
- end.tap do |c|
459
- begin
460
- c.use_time = true
461
- c.use_utc = ActiveRecord::Base.default_timezone == :utc
462
- rescue Exception => e
463
- warn "Ruby ODBC v0.99992 or higher is required."
464
- end
465
- end
278
+ odbc_connect(config)
466
279
  end
467
- @spid = _raw_select("SELECT @@SPID", :fetch => :rows).first.first
280
+ @spid = _raw_select('SELECT @@SPID', fetch: :rows).first.first
468
281
  configure_connection
469
282
  rescue
470
283
  raise unless @auto_connecting
471
284
  end
472
285
 
286
+ def dblib_connect(config)
287
+ TinyTds::Client.new(
288
+ dataserver: config[:dataserver],
289
+ host: config[:host],
290
+ port: config[:port],
291
+ username: config[:username],
292
+ password: config[:password],
293
+ database: config[:database],
294
+ tds_version: config[:tds_version],
295
+ appname: appname(config),
296
+ login_timeout: login_timeout(config),
297
+ timeout: timeout(config),
298
+ encoding: encoding(config),
299
+ azure: config[:azure]
300
+ ).tap do |client|
301
+ if config[:azure]
302
+ client.execute('SET ANSI_NULLS ON').do
303
+ client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do
304
+ client.execute('SET ANSI_NULL_DFLT_ON ON').do
305
+ client.execute('SET IMPLICIT_TRANSACTIONS OFF').do
306
+ client.execute('SET ANSI_PADDING ON').do
307
+ client.execute('SET QUOTED_IDENTIFIER ON')
308
+ client.execute('SET ANSI_WARNINGS ON').do
309
+ else
310
+ client.execute('SET ANSI_DEFAULTS ON').do
311
+ client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do
312
+ client.execute('SET IMPLICIT_TRANSACTIONS OFF').do
313
+ end
314
+ client.execute('SET TEXTSIZE 2147483647').do
315
+ client.execute('SET CONCAT_NULL_YIELDS_NULL ON').do
316
+ end
317
+ end
318
+
319
+ def appname(config)
320
+ config[:appname] || configure_application_name || Rails.application.class.name.split('::').first rescue nil
321
+ end
322
+
323
+ def login_timeout(config)
324
+ config[:login_timeout].present? ? config[:login_timeout].to_i : nil
325
+ end
326
+
327
+ def timeout(config)
328
+ config[:timeout].present? ? config[:timeout].to_i / 1000 : nil
329
+ end
330
+
331
+ def encoding(config)
332
+ config[:encoding].present? ? config[:encoding] : nil
333
+ end
334
+
335
+ def odbc_connect(config)
336
+ if config[:dsn].include?(';')
337
+ driver = ODBC::Driver.new.tap do |d|
338
+ d.name = config[:dsn_name] || 'Driver1'
339
+ d.attrs = config[:dsn].split(';').map { |atr| atr.split('=') }.reject { |kv| kv.size != 2 }.reduce({}) { |a, e| k, v = e ; a[k] = v ; a }
340
+ end
341
+ ODBC::Database.new.drvconnect(driver)
342
+ else
343
+ ODBC.connect config[:dsn], config[:username], config[:password]
344
+ end.tap do |c|
345
+ begin
346
+ c.use_time = true
347
+ c.use_utc = ActiveRecord::Base.default_timezone == :utc
348
+ rescue Exception
349
+ warn 'Ruby ODBC v0.99992 or higher is required.'
350
+ end
351
+ end
352
+ end
353
+
473
354
  # Override this method so every connection can be configured to your needs.
474
355
  # For example:
475
356
  # raw_connection_do "SET TEXTSIZE #{64.megabytes}"
@@ -486,32 +367,29 @@ module ActiveRecord
486
367
  def initialize_dateformatter
487
368
  @database_dateformat = user_options_dateformat
488
369
  a, b, c = @database_dateformat.each_char.to_a
489
- [a,b,c].each { |f| f.upcase! if f == 'y' }
370
+ [a, b, c].each { |f| f.upcase! if f == 'y' }
490
371
  dateformat = "%#{a}-%#{b}-%#{c}"
491
372
  ::Date::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
492
373
  ::Time::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
493
374
  end
494
375
 
495
- def remove_database_connections_and_rollback(database=nil)
376
+ def remove_database_connections_and_rollback(database = nil)
496
377
  database ||= current_database
497
- do_execute "ALTER DATABASE #{quote_table_name(database)} SET SINGLE_USER WITH ROLLBACK IMMEDIATE"
378
+ do_execute "ALTER DATABASE #{quote_database_name(database)} SET SINGLE_USER WITH ROLLBACK IMMEDIATE"
498
379
  begin
499
380
  yield
500
381
  ensure
501
- do_execute "ALTER DATABASE #{quote_table_name(database)} SET MULTI_USER"
382
+ do_execute "ALTER DATABASE #{quote_database_name(database)} SET MULTI_USER"
502
383
  end if block_given?
503
384
  end
504
385
 
505
386
  def with_sqlserver_error_handling
506
- begin
507
- yield
508
- rescue Exception => e
509
- case translate_exception(e,e.message)
510
- when LostConnection; retry if auto_reconnected?
511
- when DeadlockVictim; retry if retry_deadlock_victim? && open_transactions == 0
512
- end
513
- raise
387
+ yield
388
+ rescue Exception => e
389
+ case translate_exception(e, e.message)
390
+ when LostConnection then retry if auto_reconnected?
514
391
  end
392
+ raise
515
393
  end
516
394
 
517
395
  def disable_auto_reconnect
@@ -527,9 +405,9 @@ module ActiveRecord
527
405
  count = 0
528
406
  while count <= (auto_connect_duration / 2)
529
407
  result = reconnect!
530
- ActiveRecord::Base.did_retry_sqlserver_connection(self,count)
408
+ ActiveRecord::Base.did_retry_sqlserver_connection(self, count)
531
409
  return true if result
532
- sleep 2** count
410
+ sleep 2**count
533
411
  count += 1
534
412
  end
535
413
  ActiveRecord::Base.did_lose_sqlserver_connection(self)
@@ -537,10 +415,6 @@ module ActiveRecord
537
415
  ensure
538
416
  @auto_connecting = false
539
417
  end
540
-
541
- end #class SQLServerAdapter < AbstractAdapter
542
-
543
- end #module ConnectionAdapters
544
-
545
- end #module ActiveRecord
546
-
418
+ end # class SQLServerAdapter < AbstractAdapter
419
+ end # module ConnectionAdapters
420
+ end # module ActiveRecord