activerecord-sqlserver-adapter 3.2.18 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
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