sic-activerecord-sqlserver-adapter 4.0.0

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