sic-activerecord-sqlserver-adapter 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 (25) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +603 -0
  3. data/MIT-LICENSE +20 -0
  4. data/VERSION +1 -0
  5. data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +42 -0
  6. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +41 -0
  7. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +4 -0
  8. data/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb +38 -0
  9. data/lib/active_record/connection_adapters/sqlserver/core_ext/relation.rb +19 -0
  10. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +49 -0
  11. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +458 -0
  12. data/lib/active_record/connection_adapters/sqlserver/errors.rb +36 -0
  13. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +109 -0
  14. data/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +85 -0
  15. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +395 -0
  16. data/lib/active_record/connection_adapters/sqlserver/showplan.rb +67 -0
  17. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +69 -0
  18. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +25 -0
  19. data/lib/active_record/connection_adapters/sqlserver/utils.rb +32 -0
  20. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +545 -0
  21. data/lib/active_record/sqlserver_test_case.rb +17 -0
  22. data/lib/activerecord-sqlserver-adapter.rb +1 -0
  23. data/lib/arel/select_manager_sqlserver.rb +64 -0
  24. data/lib/arel/visitors/sqlserver.rb +326 -0
  25. metadata +98 -0
@@ -0,0 +1,67 @@
1
+ require 'active_record/connection_adapters/sqlserver/showplan/printer_table'
2
+ require 'active_record/connection_adapters/sqlserver/showplan/printer_xml'
3
+
4
+ module ActiveRecord
5
+ module ConnectionAdapters
6
+ module Sqlserver
7
+ module Showplan
8
+
9
+ OPTION_ALL = 'SHOWPLAN_ALL'
10
+ OPTION_TEXT = 'SHOWPLAN_TEXT'
11
+ OPTION_XML = 'SHOWPLAN_XML'
12
+ OPTIONS = [OPTION_ALL, OPTION_TEXT, OPTION_XML]
13
+
14
+ def explain(arel, binds = [])
15
+ sql = to_sql(arel)
16
+ result = with_showplan_on { do_exec_query(sql, 'EXPLAIN', binds) }
17
+ printer = showplan_printer.new(result)
18
+ printer.pp
19
+ end
20
+
21
+
22
+ protected
23
+
24
+ def with_showplan_on
25
+ set_showplan_option(true)
26
+ yield
27
+ ensure
28
+ set_showplan_option(false)
29
+ end
30
+
31
+ def set_showplan_option(enable = true)
32
+ sql = "SET #{option} #{enable ? 'ON' : 'OFF'}"
33
+ raw_connection_do(sql)
34
+ rescue Exception => e
35
+ raise ActiveRecordError, "#{option} could not be turned #{enable ? 'ON' : 'OFF'}, perhaps you do not have SHOWPLAN permissions?"
36
+ end
37
+
38
+ def option
39
+ (SQLServerAdapter.showplan_option || OPTION_ALL).tap do |opt|
40
+ raise(ArgumentError, "Unknown SHOWPLAN option #{opt.inspect} found.") if OPTIONS.exclude?(opt)
41
+ end
42
+ end
43
+
44
+ def showplan_all?
45
+ option == OPTION_ALL
46
+ end
47
+
48
+ def showplan_text?
49
+ option == OPTION_TEXT
50
+ end
51
+
52
+ def showplan_xml?
53
+ option == OPTION_XML
54
+ end
55
+
56
+ def showplan_printer
57
+ case option
58
+ when OPTION_XML then PrinterXml
59
+ when OPTION_ALL, OPTION_TEXT then PrinterTable
60
+ else PrinterTable
61
+ end
62
+ end
63
+
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,69 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module Sqlserver
4
+ module Showplan
5
+ class PrinterTable
6
+
7
+ cattr_accessor :max_column_width, :cell_padding
8
+ self.max_column_width = 50
9
+ self.cell_padding = 1
10
+
11
+ attr_reader :result
12
+
13
+ def initialize(result)
14
+ @result = result
15
+ end
16
+
17
+ def pp
18
+ @widths = compute_column_widths
19
+ @separator = build_separator
20
+ pp = []
21
+ pp << @separator
22
+ pp << build_cells(result.columns)
23
+ pp << @separator
24
+ result.rows.each do |row|
25
+ pp << build_cells(row)
26
+ end
27
+ pp << @separator
28
+ pp.join("\n") + "\n"
29
+ end
30
+
31
+ private
32
+
33
+ def compute_column_widths
34
+ [].tap do |computed_widths|
35
+ result.columns.each_with_index do |column, i|
36
+ cells_in_column = [column] + result.rows.map { |r| cast_item(r[i]) }
37
+ computed_width = cells_in_column.map(&:length).max
38
+ final_width = computed_width > max_column_width ? max_column_width : computed_width
39
+ computed_widths << final_width
40
+ end
41
+ end
42
+ end
43
+
44
+ def build_separator
45
+ '+' + @widths.map {|w| '-' * (w + (cell_padding*2))}.join('+') + '+'
46
+ end
47
+
48
+ def build_cells(items)
49
+ cells = []
50
+ items.each_with_index do |item, i|
51
+ cells << cast_item(item).ljust(@widths[i])
52
+ end
53
+ "| #{cells.join(' | ')} |"
54
+ end
55
+
56
+ def cast_item(item)
57
+ case item
58
+ when NilClass then 'NULL'
59
+ when Float then item.to_s.to(9)
60
+ else item.to_s.truncate(max_column_width)
61
+ end
62
+ end
63
+
64
+ end
65
+
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,25 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module Sqlserver
4
+ module Showplan
5
+ class PrinterXml
6
+
7
+ def initialize(result)
8
+ @result = result
9
+ end
10
+
11
+ def pp
12
+ xml = @result.rows.first.first
13
+ if defined?(Nokogiri)
14
+ Nokogiri::XML(xml).to_xml :indent => 2, :encoding => 'UTF-8'
15
+ else
16
+ xml
17
+ end
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,32 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module Sqlserver
4
+ class Utils
5
+
6
+ class << self
7
+
8
+ def unquote_string(string)
9
+ string.to_s.gsub(/\'\'/, "'")
10
+ end
11
+
12
+ def unqualify_table_name(table_name)
13
+ table_name.to_s.split('.').last.tr('[]','')
14
+ end
15
+
16
+ def unqualify_table_schema(table_name)
17
+ table_name.to_s.split('.')[-2].gsub(/[\[\]]/,'') rescue nil
18
+ end
19
+
20
+ def unqualify_db_name(table_name)
21
+ table_names = table_name.to_s.split('.')
22
+ table_names.length == 3 ? table_names.first.tr('[]','') : nil
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+
@@ -0,0 +1,545 @@
1
+ require 'base64'
2
+ require 'arel/visitors/sqlserver'
3
+ require 'arel/visitors/bind_visitor'
4
+ require 'active_record'
5
+ require 'active_record/base'
6
+ require 'active_support/concern'
7
+ require 'active_support/core_ext/string'
8
+ require 'active_record/connection_adapters/abstract_adapter'
9
+ require 'active_record/connection_adapters/sqlserver/core_ext/active_record'
10
+ require 'active_record/connection_adapters/sqlserver/core_ext/explain'
11
+ require 'active_record/connection_adapters/sqlserver/core_ext/explain_subscriber'
12
+ require 'active_record/connection_adapters/sqlserver/core_ext/relation'
13
+ require 'active_record/connection_adapters/sqlserver/database_limits'
14
+ require 'active_record/connection_adapters/sqlserver/database_statements'
15
+ require 'active_record/connection_adapters/sqlserver/errors'
16
+ require 'active_record/connection_adapters/sqlserver/schema_cache'
17
+ require 'active_record/connection_adapters/sqlserver/schema_statements'
18
+ require 'active_record/connection_adapters/sqlserver/showplan'
19
+ require 'active_record/connection_adapters/sqlserver/quoting'
20
+ require 'active_record/connection_adapters/sqlserver/utils'
21
+
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
54
+
55
+ 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
+ if value.encoding != Encoding::ASCII_8BIT
73
+ value = value.force_encoding(Encoding::ASCII_8BIT)
74
+ end
75
+ value
76
+ end
77
+
78
+ end
79
+
80
+ def is_identity?
81
+ @sqlserver_options[:is_identity]
82
+ end
83
+
84
+ def is_primary?
85
+ @sqlserver_options[:is_primary]
86
+ end
87
+
88
+ def is_utf8?
89
+ !!(@sql_type =~ /nvarchar|ntext|nchar/i)
90
+ end
91
+
92
+ def is_integer?
93
+ !!(@sql_type =~ /int/i)
94
+ end
95
+
96
+ def is_real?
97
+ !!(@sql_type =~ /real/i)
98
+ end
99
+
100
+ def sql_type_for_statement
101
+ if is_integer? || is_real?
102
+ sql_type.sub(/\((\d+)?\)/,'')
103
+ else
104
+ sql_type
105
+ end
106
+ end
107
+
108
+ def default_function
109
+ @sqlserver_options[:default_function]
110
+ end
111
+
112
+ def table_name
113
+ @sqlserver_options[:table_name]
114
+ end
115
+
116
+ def table_klass
117
+ @table_klass ||= begin
118
+ table_name.classify.constantize
119
+ rescue StandardError, NameError, LoadError
120
+ nil
121
+ end
122
+ (@table_klass && @table_klass < ActiveRecord::Base) ? @table_klass : nil
123
+ end
124
+
125
+ def database_year
126
+ @sqlserver_options[:database_year]
127
+ end
128
+
129
+
130
+ private
131
+
132
+ def extract_limit(sql_type)
133
+ case sql_type
134
+ when /^smallint/i
135
+ 2
136
+ when /^int/i
137
+ 4
138
+ when /^bigint/i
139
+ 8
140
+ when /\(max\)/, /decimal/, /numeric/
141
+ nil
142
+ else
143
+ super
144
+ end
145
+ end
146
+
147
+ def simplified_type(field_type)
148
+ case field_type
149
+ when /real/i then :float
150
+ when /money/i then :decimal
151
+ when /image/i then :binary
152
+ when /bit/i then :boolean
153
+ when /uniqueidentifier/i then :string
154
+ when /datetime/i then simplified_datetime
155
+ when /varchar\(max\)/ then :text
156
+ when /timestamp/ then :binary
157
+ else super
158
+ end
159
+ end
160
+
161
+ def simplified_datetime
162
+ if database_year >= 2008
163
+ :datetime
164
+ elsif table_klass && table_klass.coerced_sqlserver_date_columns.include?(name)
165
+ :date
166
+ elsif table_klass && table_klass.coerced_sqlserver_time_columns.include?(name)
167
+ :time
168
+ else
169
+ :datetime
170
+ end
171
+ end
172
+
173
+ end #class SQLServerColumn
174
+
175
+ class SQLServerAdapter < AbstractAdapter
176
+
177
+ include Sqlserver::Quoting
178
+ include Sqlserver::DatabaseStatements
179
+ include Sqlserver::Showplan
180
+ include Sqlserver::SchemaStatements
181
+ include Sqlserver::DatabaseLimits
182
+ include Sqlserver::Errors
183
+
184
+ VERSION = File.read(File.expand_path("../../../../VERSION",__FILE__)).strip
185
+ ADAPTER_NAME = 'SQLServer'.freeze
186
+ DATABASE_VERSION_REGEXP = /Microsoft SQL Server\s+"?(\d{4}|\w+)"?/
187
+ SUPPORTED_VERSIONS = [2005,2008,2010,2011,2012]
188
+
189
+ attr_reader :database_version, :database_year, :spid, :product_level, :product_version, :edition
190
+
191
+ cattr_accessor :native_text_database_type, :native_binary_database_type, :native_string_database_type,
192
+ :enable_default_unicode_types, :auto_connect, :cs_equality_operator,
193
+ :lowercase_schema_reflection, :auto_connect_duration, :showplan_option
194
+
195
+ self.enable_default_unicode_types = true
196
+
197
+ class BindSubstitution < Arel::Visitors::SQLServer # :nodoc:
198
+ include Arel::Visitors::BindVisitor
199
+ end
200
+
201
+ def initialize(connection, logger, pool, config)
202
+ super(connection, logger, pool)
203
+ # AbstractAdapter Responsibility
204
+ @schema_cache = Sqlserver::SchemaCache.new self
205
+ @visitor = Arel::Visitors::SQLServer.new self
206
+ # Our Responsibility
207
+ @config = config
208
+ @connection_options = config
209
+ connect
210
+ @database_version = select_value 'SELECT @@version', 'SCHEMA'
211
+ @database_year = begin
212
+ if @database_version =~ /Azure/i
213
+ @sqlserver_azure = true
214
+ @database_version.match(/\s-\s([0-9.]+)/)[1]
215
+ year = 2012
216
+ else
217
+ year = DATABASE_VERSION_REGEXP.match(@database_version)[1]
218
+ year == "Denali" ? 2011 : year.to_i
219
+ end
220
+ rescue
221
+ 0
222
+ end
223
+ @product_level = select_value "SELECT CAST(SERVERPROPERTY('productlevel') AS VARCHAR(128))", 'SCHEMA'
224
+ @product_version = select_value "SELECT CAST(SERVERPROPERTY('productversion') AS VARCHAR(128))", 'SCHEMA'
225
+ @edition = select_value "SELECT CAST(SERVERPROPERTY('edition') AS VARCHAR(128))", 'SCHEMA'
226
+ initialize_dateformatter
227
+ use_database
228
+ unless (@sqlserver_azure == true || SUPPORTED_VERSIONS.include?(@database_year))
229
+ raise NotImplementedError, "Currently, only #{SUPPORTED_VERSIONS.to_sentence} are supported. We got back #{@database_version}."
230
+ end
231
+ end
232
+
233
+ # === Abstract Adapter ========================================== #
234
+
235
+ def adapter_name
236
+ ADAPTER_NAME
237
+ end
238
+
239
+ def supports_migrations?
240
+ true
241
+ end
242
+
243
+ def supports_primary_key?
244
+ true
245
+ end
246
+
247
+ def supports_count_distinct?
248
+ true
249
+ end
250
+
251
+ def supports_ddl_transactions?
252
+ true
253
+ end
254
+
255
+ def supports_bulk_alter?
256
+ false
257
+ end
258
+
259
+ def supports_savepoints?
260
+ true
261
+ end
262
+
263
+ def supports_index_sort_order?
264
+ true
265
+ end
266
+
267
+ def supports_explain?
268
+ true
269
+ end
270
+
271
+ def disable_referential_integrity
272
+ do_execute "EXEC sp_MSforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'"
273
+ yield
274
+ ensure
275
+ do_execute "EXEC sp_MSforeachtable 'ALTER TABLE ? CHECK CONSTRAINT ALL'"
276
+ end
277
+
278
+ # === Abstract Adapter (Connection Management) ================== #
279
+
280
+ def active?
281
+ case @connection_options[:mode]
282
+ when :dblib
283
+ return @connection.active?
284
+ end
285
+ raw_connection_do("SELECT 1")
286
+ true
287
+ rescue *lost_connection_exceptions
288
+ false
289
+ end
290
+
291
+ def reconnect!
292
+ reset_transaction
293
+ disconnect!
294
+ connect
295
+ active?
296
+ end
297
+
298
+ def disconnect!
299
+ reset_transaction
300
+ @spid = nil
301
+ case @connection_options[:mode]
302
+ when :dblib
303
+ @connection.close rescue nil
304
+ when :odbc
305
+ @connection.disconnect rescue nil
306
+ end
307
+ end
308
+
309
+ def reset!
310
+ remove_database_connections_and_rollback { }
311
+ end
312
+
313
+ # === Abstract Adapter (Misc Support) =========================== #
314
+
315
+ def pk_and_sequence_for(table_name)
316
+ idcol = identity_column(table_name)
317
+ idcol ? [idcol.name,nil] : nil
318
+ end
319
+
320
+ def primary_key(table_name)
321
+ identity_column(table_name).try(:name) || schema_cache.columns(table_name).detect(&:is_primary?).try(:name)
322
+ end
323
+
324
+ # === SQLServer Specific (DB Reflection) ======================== #
325
+
326
+ def sqlserver?
327
+ true
328
+ end
329
+
330
+ def sqlserver_2005?
331
+ @database_year == 2005
332
+ end
333
+
334
+ def sqlserver_2008?
335
+ @database_year == 2008
336
+ end
337
+
338
+ def sqlserver_2011?
339
+ @database_year == 2011
340
+ end
341
+
342
+ def sqlserver_2012?
343
+ @database_year == 2012
344
+ end
345
+
346
+ def sqlserver_azure?
347
+ @sqlserver_azure
348
+ end
349
+
350
+ def version
351
+ self.class::VERSION
352
+ end
353
+
354
+ def inspect
355
+ "#<#{self.class} version: #{version}, year: #{@database_year}, product_level: #{@product_level.inspect}, product_version: #{@product_version.inspect}, edition: #{@edition.inspect}, connection_options: #{@connection_options.inspect}>"
356
+ end
357
+
358
+ def auto_connect
359
+ @@auto_connect.is_a?(FalseClass) ? false : true
360
+ end
361
+
362
+ def auto_connect_duration
363
+ @@auto_connect_duration ||= 10
364
+ end
365
+
366
+ def native_string_database_type
367
+ @@native_string_database_type || (enable_default_unicode_types ? 'nvarchar' : 'varchar')
368
+ end
369
+
370
+ def native_text_database_type
371
+ @@native_text_database_type || enable_default_unicode_types ? 'nvarchar(max)' : 'varchar(max)'
372
+ end
373
+
374
+ def native_time_database_type
375
+ sqlserver_2005? ? 'datetime' : 'time'
376
+ end
377
+
378
+ def native_date_database_type
379
+ sqlserver_2005? ? 'datetime' : 'date'
380
+ end
381
+
382
+ def native_binary_database_type
383
+ @@native_binary_database_type || 'varbinary(max)'
384
+ end
385
+
386
+ def cs_equality_operator
387
+ @@cs_equality_operator || 'COLLATE Latin1_General_CS_AS_WS'
388
+ end
389
+
390
+ protected
391
+
392
+ # === Abstract Adapter (Misc Support) =========================== #
393
+
394
+ def translate_exception(e, message)
395
+ case message
396
+ when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i
397
+ RecordNotUnique.new(message,e)
398
+ when /conflicted with the foreign key constraint/i
399
+ InvalidForeignKey.new(message,e)
400
+ when /has been chosen as the deadlock victim/i
401
+ DeadlockVictim.new(message,e)
402
+ when *lost_connection_messages
403
+ LostConnection.new(message,e)
404
+ else
405
+ super
406
+ end
407
+ end
408
+
409
+ # === SQLServer Specific (Connection Management) ================ #
410
+
411
+ def connect
412
+ config = @connection_options
413
+ @connection = case config[:mode]
414
+ 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
449
+ 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
466
+ end
467
+ @spid = _raw_select("SELECT @@SPID", :fetch => :rows).first.first
468
+ configure_connection
469
+ rescue
470
+ raise unless @auto_connecting
471
+ end
472
+
473
+ # Override this method so every connection can be configured to your needs.
474
+ # For example:
475
+ # raw_connection_do "SET TEXTSIZE #{64.megabytes}"
476
+ # raw_connection_do "SET CONCAT_NULL_YIELDS_NULL ON"
477
+ def configure_connection
478
+ end
479
+
480
+ # Override this method so every connection can have a unique name. Max 30 characters. Used by TinyTDS only.
481
+ # For example:
482
+ # "myapp_#{$$}_#{Thread.current.object_id}".to(29)
483
+ def configure_application_name
484
+ end
485
+
486
+ def initialize_dateformatter
487
+ @database_dateformat = user_options_dateformat
488
+ a, b, c = @database_dateformat.each_char.to_a
489
+ [a,b,c].each { |f| f.upcase! if f == 'y' }
490
+ dateformat = "%#{a}-%#{b}-%#{c}"
491
+ ::Date::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
492
+ ::Time::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
493
+ end
494
+
495
+ def remove_database_connections_and_rollback(database=nil)
496
+ database ||= current_database
497
+ do_execute "ALTER DATABASE #{quote_table_name(database)} SET SINGLE_USER WITH ROLLBACK IMMEDIATE"
498
+ begin
499
+ yield
500
+ ensure
501
+ do_execute "ALTER DATABASE #{quote_table_name(database)} SET MULTI_USER"
502
+ end if block_given?
503
+ end
504
+
505
+ 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
+ end
512
+ raise
513
+ end
514
+ end
515
+
516
+ def disable_auto_reconnect
517
+ old_auto_connect, self.class.auto_connect = self.class.auto_connect, false
518
+ yield
519
+ ensure
520
+ self.class.auto_connect = old_auto_connect
521
+ end
522
+
523
+ def auto_reconnected?
524
+ return false unless auto_connect
525
+ @auto_connecting = true
526
+ count = 0
527
+ while count <= (auto_connect_duration / 2)
528
+ result = reconnect!
529
+ ActiveRecord::Base.did_retry_sqlserver_connection(self,count)
530
+ return true if result
531
+ sleep 2** count
532
+ count += 1
533
+ end
534
+ ActiveRecord::Base.did_lose_sqlserver_connection(self)
535
+ false
536
+ ensure
537
+ @auto_connecting = false
538
+ end
539
+
540
+ end #class SQLServerAdapter < AbstractAdapter
541
+
542
+ end #module ConnectionAdapters
543
+
544
+ end #module ActiveRecord
545
+