asa-2000 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,28 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module Sqlserver
4
+ class Utils
5
+
6
+ class << self
7
+
8
+ def unqualify_table_name(table_name)
9
+ table_name.to_s.split('.').last.tr('[]','')
10
+ end
11
+
12
+ def unqualify_table_schema(table_name)
13
+ table_name.to_s.split('.')[-2].gsub(/[\[\]]/,'') rescue nil
14
+ end
15
+
16
+ def unqualify_db_name(table_name)
17
+ table_names = table_name.to_s.split('.')
18
+ table_names.length == 3 ? table_names.first.tr('[]','') : nil
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+
@@ -0,0 +1,11 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module Sqlserver
4
+ module Version
5
+
6
+ VERSION = '3.2.0.rc1'
7
+
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,516 @@
1
+ require 'arel/visitors/sqlserver'
2
+ require 'active_record'
3
+ require 'active_record/connection_adapters/abstract_adapter'
4
+ require 'active_record/connection_adapters/sqlserver/core_ext/active_record'
5
+ require 'active_record/connection_adapters/sqlserver/core_ext/database_statements'
6
+ require 'active_record/connection_adapters/sqlserver/database_limits'
7
+ require 'active_record/connection_adapters/sqlserver/database_statements'
8
+ require 'active_record/connection_adapters/sqlserver/errors'
9
+ require 'active_record/connection_adapters/sqlserver/schema_cache'
10
+ require 'active_record/connection_adapters/sqlserver/schema_statements'
11
+ require 'active_record/connection_adapters/sqlserver/quoting'
12
+ require 'active_record/connection_adapters/sqlserver/utils'
13
+ require 'active_record/connection_adapters/sqlserver/version'
14
+ require 'active_support/core_ext/string'
15
+ require 'base64'
16
+
17
+ module ActiveRecord
18
+
19
+ class Base
20
+
21
+ def self.sqlserver_connection(config) #:nodoc:
22
+ config = config.symbolize_keys
23
+ config.reverse_merge! :mode => :dblib, :host => 'localhost', :username => 'sa', :password => ''
24
+ mode = config[:mode].to_s.downcase.underscore.to_sym
25
+ case mode
26
+ when :dblib
27
+ require 'tiny_tds'
28
+ warn("TinyTds v0.4.3 or higher required. Using #{TinyTds::VERSION}") unless TinyTds::Client.instance_methods.map(&:to_s).include?("active?")
29
+ when :odbc
30
+ raise ArgumentError, 'Missing :dsn configuration.' unless config.has_key?(:dsn)
31
+ require 'odbc'
32
+ require 'active_record/connection_adapters/sqlserver/core_ext/odbc'
33
+ else
34
+ raise ArgumentError, "Unknown connection mode in #{config.inspect}."
35
+ end
36
+ ConnectionAdapters::SQLServerAdapter.new(nil, logger, nil, config.merge(:mode=>mode))
37
+ end
38
+
39
+ protected
40
+
41
+ def self.did_retry_sqlserver_connection(connection,count)
42
+ logger.info "CONNECTION RETRY: #{connection.class.name} retry ##{count}."
43
+ end
44
+
45
+ def self.did_lose_sqlserver_connection(connection)
46
+ logger.info "CONNECTION LOST: #{connection.class.name}"
47
+ end
48
+
49
+ end
50
+
51
+ module ConnectionAdapters
52
+
53
+ class SQLServerColumn < Column
54
+
55
+ def initialize(name, default, sql_type = nil, null = true, sqlserver_options = {})
56
+ @sqlserver_options = sqlserver_options.symbolize_keys
57
+ super(name, default, sql_type, null)
58
+ @primary = @sqlserver_options[:is_identity] || @sqlserver_options[:is_primary]
59
+ end
60
+
61
+ class << self
62
+
63
+ def string_to_binary(value)
64
+ "0x#{value.unpack("H*")[0]}"
65
+ end
66
+
67
+ def binary_to_string(value)
68
+ value =~ /[^[:xdigit:]]/ ? value : [value].pack('H*')
69
+ end
70
+
71
+ end
72
+
73
+ def is_identity?
74
+ @sqlserver_options[:is_identity]
75
+ end
76
+
77
+ def is_primary?
78
+ @sqlserver_options[:is_primary]
79
+ end
80
+
81
+ def is_utf8?
82
+ !!(@sql_type =~ /nvarchar|ntext|nchar/i)
83
+ end
84
+
85
+ def is_integer?
86
+ !!(@sql_type =~ /int/i)
87
+ end
88
+
89
+ def is_real?
90
+ !!(@sql_type =~ /real/i)
91
+ end
92
+
93
+ def sql_type_for_statement
94
+ if is_integer? || is_real?
95
+ sql_type.sub(/\(\d+\)/,'')
96
+ else
97
+ sql_type
98
+ end
99
+ end
100
+
101
+ def default_function
102
+ @sqlserver_options[:default_function]
103
+ end
104
+
105
+ def table_name
106
+ @sqlserver_options[:table_name]
107
+ end
108
+
109
+ def table_klass
110
+ @table_klass ||= begin
111
+ table_name.classify.constantize
112
+ rescue StandardError, NameError, LoadError
113
+ nil
114
+ end
115
+ (@table_klass && @table_klass < ActiveRecord::Base) ? @table_klass : nil
116
+ end
117
+
118
+ def database_year
119
+ @sqlserver_options[:database_year]
120
+ end
121
+
122
+
123
+ private
124
+
125
+ def extract_limit(sql_type)
126
+ case sql_type
127
+ when /^smallint/i
128
+ 2
129
+ when /^int/i
130
+ 4
131
+ when /^bigint/i
132
+ 8
133
+ when /\(max\)/, /decimal/, /numeric/
134
+ nil
135
+ else
136
+ super
137
+ end
138
+ end
139
+
140
+ def simplified_type(field_type)
141
+ case field_type
142
+ when /real/i then :float
143
+ when /money/i then :decimal
144
+ when /image/i then :binary
145
+ when /bit/i then :boolean
146
+ when /uniqueidentifier/i then :string
147
+ when /datetime/i then simplified_datetime
148
+ when /varchar\(max\)/ then :text
149
+ when /timestamp/ then :binary
150
+ else super
151
+ end
152
+ end
153
+
154
+ def simplified_datetime
155
+ if database_year >= 2008
156
+ :datetime
157
+ elsif table_klass && table_klass.coerced_sqlserver_date_columns.include?(name)
158
+ :date
159
+ elsif table_klass && table_klass.coerced_sqlserver_time_columns.include?(name)
160
+ :time
161
+ else
162
+ :datetime
163
+ end
164
+ end
165
+
166
+ end #class SQLServerColumn
167
+
168
+ class SQLServerAdapter < AbstractAdapter
169
+
170
+ include Sqlserver::Quoting
171
+ include Sqlserver::DatabaseStatements
172
+ include Sqlserver::SchemaStatements
173
+ include Sqlserver::DatabaseLimits
174
+ include Sqlserver::Errors
175
+ include Sqlserver::Version
176
+
177
+ ADAPTER_NAME = 'SQLServer'.freeze
178
+ DATABASE_VERSION_REGEXP = /Microsoft SQL Server\s+"?(\d{4}|\w+)"?/
179
+ SUPPORTED_VERSIONS = [2005,2008,2010,2011].freeze
180
+
181
+ attr_reader :database_version, :database_year, :spid, :product_level, :product_version, :edition
182
+
183
+ cattr_accessor :native_text_database_type, :native_binary_database_type, :native_string_database_type,
184
+ :log_info_schema_queries, :enable_default_unicode_types, :auto_connect, :retry_deadlock_victim,
185
+ :cs_equality_operator, :lowercase_schema_reflection, :auto_connect_duration
186
+
187
+ self.enable_default_unicode_types = true
188
+
189
+
190
+ def initialize(connection, logger, pool, config)
191
+ super(connection, logger, pool)
192
+ # AbstractAdapter Responsibility
193
+ @schema_cache = Sqlserver::SchemaCache.new self
194
+ @visitor = Arel::Visitors::SQLServer.new self
195
+ # Our Responsibility
196
+ @connection_options = config
197
+ connect
198
+ @database_version = info_schema_query { select_value('SELECT @@version') }
199
+ @database_year = begin
200
+ if @database_version =~ /Microsoft SQL Azure/i
201
+ @sqlserver_azure = true
202
+ @database_version.match(/\s(\d{4})\s/)[1].to_i
203
+ else
204
+ year = DATABASE_VERSION_REGEXP.match(@database_version)[1]
205
+ year == "Denali" ? 2011 : year.to_i
206
+ end
207
+ rescue
208
+ 0
209
+ end
210
+ @product_level = info_schema_query { select_value("SELECT CAST(SERVERPROPERTY('productlevel') AS VARCHAR(128))") }
211
+ @product_version = info_schema_query { select_value("SELECT CAST(SERVERPROPERTY('productversion') AS VARCHAR(128))") }
212
+ @edition = info_schema_query { select_value("SELECT CAST(SERVERPROPERTY('edition') AS VARCHAR(128))") }
213
+ initialize_dateformatter
214
+ use_database
215
+ unless SUPPORTED_VERSIONS.include?(@database_year)
216
+ #raise NotImplementedError, "Currently, only #{SUPPORTED_VERSIONS.to_sentence} are supported. We got back #{@database_version}."
217
+ end
218
+ end
219
+
220
+ # === Abstract Adapter ========================================== #
221
+
222
+ def adapter_name
223
+ ADAPTER_NAME
224
+ end
225
+
226
+ def supports_migrations?
227
+ true
228
+ end
229
+
230
+ def supports_primary_key?
231
+ true
232
+ end
233
+
234
+ def supports_count_distinct?
235
+ true
236
+ end
237
+
238
+ def supports_ddl_transactions?
239
+ true
240
+ end
241
+
242
+ def supports_savepoints?
243
+ true
244
+ end
245
+
246
+ def disable_referential_integrity
247
+ do_execute "EXEC sp_MSforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'"
248
+ yield
249
+ ensure
250
+ do_execute "EXEC sp_MSforeachtable 'ALTER TABLE ? CHECK CONSTRAINT ALL'"
251
+ end
252
+
253
+ # === Abstract Adapter (Connection Management) ================== #
254
+
255
+ def active?
256
+ case @connection_options[:mode]
257
+ when :dblib
258
+ return @connection.active?
259
+ end
260
+ raw_connection_do("SELECT 1")
261
+ true
262
+ rescue *lost_connection_exceptions
263
+ false
264
+ end
265
+
266
+ def reconnect!
267
+ disconnect!
268
+ connect
269
+ active?
270
+ end
271
+
272
+ def disconnect!
273
+ @spid = nil
274
+ case @connection_options[:mode]
275
+ when :dblib
276
+ @connection.close rescue nil
277
+ when :odbc
278
+ @connection.disconnect rescue nil
279
+ end
280
+ end
281
+
282
+ def reset!
283
+ remove_database_connections_and_rollback { }
284
+ end
285
+
286
+ # === Abstract Adapter (Misc Support) =========================== #
287
+
288
+ def pk_and_sequence_for(table_name)
289
+ idcol = identity_column(table_name)
290
+ idcol ? [idcol.name,nil] : nil
291
+ end
292
+
293
+ def primary_key(table_name)
294
+ identity_column(table_name).try(:name) || schema_cache.columns[table_name].detect(&:is_primary?).try(:name)
295
+ end
296
+
297
+ # === SQLServer Specific (DB Reflection) ======================== #
298
+
299
+ def sqlserver?
300
+ true
301
+ end
302
+
303
+ def sqlserver_2005?
304
+ @database_year == 2005
305
+ end
306
+
307
+ def sqlserver_2008?
308
+ @database_year == 2008
309
+ end
310
+
311
+ def sqlserver_2011?
312
+ @database_year == 2011
313
+ end
314
+
315
+ def sqlserver_azure?
316
+ @sqlserver_azure
317
+ end
318
+
319
+ def version
320
+ self.class::VERSION
321
+ end
322
+
323
+ def inspect
324
+ "#<#{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}>"
325
+ end
326
+
327
+ def auto_connect
328
+ @@auto_connect.is_a?(FalseClass) ? false : true
329
+ end
330
+
331
+ def auto_connect_duration
332
+ @@auto_connect_duration ||= 10
333
+ end
334
+
335
+ def retry_deadlock_victim
336
+ @@retry_deadlock_victim.is_a?(FalseClass) ? false : true
337
+ end
338
+ alias :retry_deadlock_victim? :retry_deadlock_victim
339
+
340
+ def native_string_database_type
341
+ @@native_string_database_type || (enable_default_unicode_types ? 'nvarchar' : 'varchar')
342
+ end
343
+
344
+ def native_text_database_type
345
+ @@native_text_database_type || enable_default_unicode_types ? 'nvarchar(max)' : 'varchar(max)'
346
+ end
347
+
348
+ def native_time_database_type
349
+ sqlserver_2005? ? 'datetime' : 'time'
350
+ end
351
+
352
+ def native_date_database_type
353
+ sqlserver_2005? ? 'datetime' : 'date'
354
+ end
355
+
356
+ def native_binary_database_type
357
+ @@native_binary_database_type || 'varbinary(max)'
358
+ end
359
+
360
+ def cs_equality_operator
361
+ @@cs_equality_operator || 'COLLATE Latin1_General_CS_AS_WS'
362
+ end
363
+
364
+ protected
365
+
366
+ # === Abstract Adapter (Misc Support) =========================== #
367
+
368
+ def translate_exception(e, message)
369
+ case message
370
+ when /cannot insert duplicate key .* with unique index/i
371
+ RecordNotUnique.new(message,e)
372
+ when /conflicted with the foreign key constraint/i
373
+ InvalidForeignKey.new(message,e)
374
+ when /has been chosen as the deadlock victim/i
375
+ DeadlockVictim.new(message,e)
376
+ when *lost_connection_messages
377
+ LostConnection.new(message,e)
378
+ else
379
+ super
380
+ end
381
+ end
382
+
383
+ # === SQLServer Specific (Connection Management) ================ #
384
+
385
+ def connect
386
+ config = @connection_options
387
+ @connection = case config[:mode]
388
+ when :dblib
389
+ appname = config[:appname] || configure_application_name || Rails.application.class.name.split('::').first rescue nil
390
+ login_timeout = config[:login_timeout].present? ? config[:login_timeout].to_i : nil
391
+ timeout = config[:timeout].present? ? config[:timeout].to_i/1000 : nil
392
+ encoding = config[:encoding].present? ? config[:encoding] : nil
393
+ TinyTds::Client.new({
394
+ :dataserver => config[:dataserver],
395
+ :host => config[:host],
396
+ :port => config[:port],
397
+ :username => config[:username],
398
+ :password => config[:password],
399
+ :database => config[:database],
400
+ :appname => appname,
401
+ :login_timeout => login_timeout,
402
+ :timeout => timeout,
403
+ :encoding => encoding,
404
+ :azure => config[:azure]
405
+ }).tap do |client|
406
+ if config[:azure]
407
+ client.execute("SET ANSI_NULLS ON").do
408
+ client.execute("SET CURSOR_CLOSE_ON_COMMIT OFF").do
409
+ client.execute("SET ANSI_NULL_DFLT_ON ON").do
410
+ client.execute("SET IMPLICIT_TRANSACTIONS OFF").do
411
+ client.execute("SET ANSI_PADDING ON").do
412
+ client.execute("SET QUOTED_IDENTIFIER ON")
413
+ client.execute("SET ANSI_WARNINGS ON").do
414
+ else
415
+ client.execute("SET ANSI_DEFAULTS ON").do
416
+ client.execute("SET CURSOR_CLOSE_ON_COMMIT OFF").do
417
+ client.execute("SET IMPLICIT_TRANSACTIONS OFF").do
418
+ end
419
+ end
420
+ when :odbc
421
+ if config[:dsn].include?(';')
422
+ driver = ODBC::Driver.new.tap do |d|
423
+ d.name = config[:dsn_name] || 'Driver1'
424
+ d.attrs = config[:dsn].split(';').map{ |atr| atr.split('=') }.reject{ |kv| kv.size != 2 }.inject({}){ |h,kv| k,v = kv ; h[k] = v ; h }
425
+ end
426
+ ODBC::Database.new.drvconnect(driver)
427
+ else
428
+ ODBC.connect config[:dsn], config[:username], config[:password]
429
+ end.tap do |c|
430
+ begin
431
+ c.use_time = true
432
+ c.use_utc = ActiveRecord::Base.default_timezone == :utc
433
+ rescue Exception => e
434
+ warn "Ruby ODBC v0.99992 or higher is required."
435
+ end
436
+ end
437
+ end
438
+ @spid = _raw_select("SELECT @@SPID", :fetch => :rows).first.first
439
+ configure_connection
440
+ rescue
441
+ raise unless @auto_connecting
442
+ end
443
+
444
+ # Override this method so every connection can be configured to your needs.
445
+ # For example:
446
+ # raw_connection_do "SET TEXTSIZE #{64.megabytes}"
447
+ # raw_connection_do "SET CONCAT_NULL_YIELDS_NULL ON"
448
+ def configure_connection
449
+ end
450
+
451
+ # Override this method so every connection can have a unique name. Max 30 characters. Used by TinyTDS only.
452
+ # For example:
453
+ # "myapp_#{$$}_#{Thread.current.object_id}".to(29)
454
+ def configure_application_name
455
+ end
456
+
457
+ def initialize_dateformatter
458
+ @database_dateformat = user_options_dateformat
459
+ a, b, c = @database_dateformat.each_char.to_a
460
+ [a,b,c].each { |f| f.upcase! if f == 'y' }
461
+ dateformat = "%#{a}-%#{b}-%#{c}"
462
+ ::Date::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
463
+ ::Time::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
464
+ end
465
+
466
+ def remove_database_connections_and_rollback(database=nil)
467
+ database ||= current_database
468
+ do_execute "ALTER DATABASE #{quote_table_name(database)} SET SINGLE_USER WITH ROLLBACK IMMEDIATE"
469
+ begin
470
+ yield
471
+ ensure
472
+ do_execute "ALTER DATABASE #{quote_table_name(database)} SET MULTI_USER"
473
+ end if block_given?
474
+ end
475
+
476
+ def with_sqlserver_error_handling
477
+ begin
478
+ yield
479
+ rescue Exception => e
480
+ case translate_exception(e,e.message)
481
+ when LostConnection; retry if auto_reconnected?
482
+ when DeadlockVictim; retry if retry_deadlock_victim? && open_transactions == 0
483
+ end
484
+ raise
485
+ end
486
+ end
487
+
488
+ def disable_auto_reconnect
489
+ old_auto_connect, self.class.auto_connect = self.class.auto_connect, false
490
+ yield
491
+ ensure
492
+ self.class.auto_connect = old_auto_connect
493
+ end
494
+
495
+ def auto_reconnected?
496
+ return false unless auto_connect
497
+ @auto_connecting = true
498
+ count = 0
499
+ while count <= (auto_connect_duration / 2)
500
+ sleep 2** count
501
+ ActiveRecord::Base.did_retry_sqlserver_connection(self,count)
502
+ return true if reconnect!
503
+ count += 1
504
+ end
505
+ ActiveRecord::Base.did_lose_sqlserver_connection(self)
506
+ false
507
+ ensure
508
+ @auto_connecting = false
509
+ end
510
+
511
+ end #class SQLServerAdapter < AbstractAdapter
512
+
513
+ end #module ConnectionAdapters
514
+
515
+ end #module ActiveRecord
516
+