asa-2000 1.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.
@@ -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
+