activerecord-sqlserver-adapter-2000 3.0.15

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,58 @@
1
+ require 'active_record/version'
2
+ require 'active_support/core_ext/class/inheritable_attributes'
3
+
4
+ module ActiveRecord
5
+ module ConnectionAdapters
6
+ module Sqlserver
7
+ module CoreExt
8
+ module ActiveRecord
9
+
10
+ def self.included(klass)
11
+ klass.extend ClassMethods
12
+ class << klass
13
+ alias_method_chain :reset_column_information, :sqlserver_cache_support
14
+ end
15
+ end
16
+
17
+ module ClassMethods
18
+
19
+ def execute_procedure(proc_name, *variables)
20
+ if connection.respond_to?(:execute_procedure)
21
+ connection.execute_procedure(proc_name,*variables)
22
+ else
23
+ []
24
+ end
25
+ end
26
+
27
+ def coerce_sqlserver_date(*attributes)
28
+ write_inheritable_attribute :coerced_sqlserver_date_columns, Set.new(attributes.map(&:to_s))
29
+ end
30
+
31
+ def coerce_sqlserver_time(*attributes)
32
+ write_inheritable_attribute :coerced_sqlserver_time_columns, Set.new(attributes.map(&:to_s))
33
+ end
34
+
35
+ def coerced_sqlserver_date_columns
36
+ read_inheritable_attribute(:coerced_sqlserver_date_columns) || []
37
+ end
38
+
39
+ def coerced_sqlserver_time_columns
40
+ read_inheritable_attribute(:coerced_sqlserver_time_columns) || []
41
+ end
42
+
43
+ def reset_column_information_with_sqlserver_cache_support
44
+ connection.send(:initialize_sqlserver_caches) if connection.respond_to?(:sqlserver?)
45
+ reset_column_information_without_sqlserver_cache_support
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+
57
+ ActiveRecord::Base.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ActiveRecord
58
+
@@ -0,0 +1,57 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module Sqlserver
4
+ module CoreExt
5
+ module ODBC
6
+
7
+ module TimeStamp
8
+
9
+ def to_sqlserver_string
10
+ date, time, nanoseconds = to_s.split(' ')
11
+ "#{date} #{time}.#{sprintf("%03d",nanoseconds.to_i/1000000)}"
12
+ end
13
+
14
+ end
15
+
16
+ module Statement
17
+
18
+ def finished?
19
+ begin
20
+ connected?
21
+ false
22
+ rescue *Database.parent_modules_error_exceptions
23
+ true
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ module Database
30
+
31
+ def self.parent_modules
32
+ @parent_module ||= ['ODBC','ODBC_UTF8','ODBC_NONE'].map{ |odbc_ns| odbc_ns.constantize rescue nil }.compact
33
+ end
34
+
35
+ def self.parent_modules_error_exceptions
36
+ @parent_modules_error_exceptions ||= parent_modules.map { |odbc_ns| "::#{odbc_ns}::Error".constantize }
37
+ end
38
+
39
+ def run_block(*args)
40
+ yield sth = run(*args)
41
+ sth.drop
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ ['ODBC','ODBC_UTF8','ODBC_NONE'].map{ |odbc_ns| odbc_ns.constantize rescue nil }.compact.each do |ns|
53
+ ns::TimeStamp.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ODBC::TimeStamp
54
+ ns::Statement.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ODBC::Statement
55
+ ns::Database.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ODBC::Database
56
+ end
57
+
@@ -0,0 +1,49 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module Sqlserver
4
+ module DatabaseLimits
5
+
6
+ def table_alias_length
7
+ 128
8
+ end
9
+
10
+ def column_name_length
11
+ 128
12
+ end
13
+
14
+ def table_name_length
15
+ 128
16
+ end
17
+
18
+ def index_name_length
19
+ 128
20
+ end
21
+
22
+ def columns_per_table
23
+ 1024
24
+ end
25
+
26
+ def indexes_per_table
27
+ 999
28
+ end
29
+
30
+ def columns_per_multicolumn_index
31
+ 16
32
+ end
33
+
34
+ def in_clause_length
35
+ 65536
36
+ end
37
+
38
+ def sql_query_length
39
+ 65536 * 4096
40
+ end
41
+
42
+ def joins_per_query
43
+ 256
44
+ end
45
+
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,414 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module Sqlserver
4
+ module DatabaseStatements
5
+
6
+ def select_one(sql, name = nil)
7
+ result = raw_select sql, name, :fetch => :one
8
+ (result && result.first.present?) ? result.first : nil
9
+ end
10
+
11
+ def select_rows(sql, name = nil)
12
+ raw_select sql, name, :fetch => :rows
13
+ end
14
+
15
+ def execute(sql, name = nil, skip_logging = false)
16
+ if id_insert_table_name = query_requires_identity_insert?(sql)
17
+ with_identity_insert_enabled(id_insert_table_name) { do_execute(sql,name) }
18
+ else
19
+ do_execute(sql,name)
20
+ end
21
+ end
22
+
23
+ def outside_transaction?
24
+ info_schema_query { select_value("SELECT @@TRANCOUNT") == 0 }
25
+ end
26
+
27
+ def begin_db_transaction
28
+ do_execute "BEGIN TRANSACTION"
29
+ end
30
+
31
+ def commit_db_transaction
32
+ do_execute "COMMIT TRANSACTION"
33
+ end
34
+
35
+ def rollback_db_transaction
36
+ do_execute "ROLLBACK TRANSACTION" rescue nil
37
+ end
38
+
39
+ def create_savepoint
40
+ do_execute "SAVE TRANSACTION #{current_savepoint_name}"
41
+ end
42
+
43
+ def release_savepoint
44
+ end
45
+
46
+ def rollback_to_savepoint
47
+ do_execute "ROLLBACK TRANSACTION #{current_savepoint_name}"
48
+ end
49
+
50
+ def add_limit_offset!(sql, options)
51
+ raise NotImplementedError, 'This has been moved to the SQLServerCompiler in Arel.'
52
+ end
53
+
54
+ def empty_insert_statement_value
55
+ "DEFAULT VALUES"
56
+ end
57
+
58
+ def case_sensitive_equality_operator
59
+ cs_equality_operator
60
+ end
61
+
62
+ def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
63
+ match_data = where_sql.match(/^(.*?[\]\) ])WHERE[\[\( ]/)
64
+ limit = match_data[1]
65
+ where_sql.sub!(limit,'')
66
+ "WHERE #{quoted_primary_key} IN (SELECT #{limit} #{quoted_primary_key} FROM #{quoted_table_name} #{where_sql})"
67
+ end
68
+
69
+ # === SQLServer Specific ======================================== #
70
+
71
+ def execute_procedure(proc_name, *variables)
72
+ vars = variables.map{ |v| quote(v) }.join(', ')
73
+ sql = "EXEC #{proc_name} #{vars}".strip
74
+ name = 'Execute Procedure'
75
+ log(sql, name) do
76
+ case @connection_options[:mode]
77
+ when :dblib
78
+ result = @connection.execute(sql)
79
+ result.each(:as => :hash, :cache_rows => true) do |row|
80
+ r = row.with_indifferent_access
81
+ yield(r) if block_given?
82
+ end
83
+ result.each.map{ |row| row.is_a?(Hash) ? row.with_indifferent_access : row }
84
+ when :odbc
85
+ results = []
86
+ raw_connection_run(sql) do |handle|
87
+ get_rows = lambda {
88
+ rows = handle_to_names_and_values handle, :fetch => :all
89
+ rows.each_with_index { |r,i| rows[i] = r.with_indifferent_access }
90
+ results << rows
91
+ }
92
+ get_rows.call
93
+ while handle_more_results?(handle)
94
+ get_rows.call
95
+ end
96
+ end
97
+ results.many? ? results : results.first
98
+ when :adonet
99
+ results = []
100
+ results << select(sql, name).map { |r| r.with_indifferent_access }
101
+ results.many? ? results : results.first
102
+ end
103
+ end
104
+ end
105
+
106
+ def use_database(database=nil)
107
+ return if sqlserver_azure?
108
+ database ||= @connection_options[:database]
109
+ do_execute "USE #{quote_table_name(database)}" unless database.blank?
110
+ end
111
+
112
+ def user_options
113
+ info_schema_query do
114
+ select_rows("dbcc useroptions").inject(HashWithIndifferentAccess.new) do |values,row|
115
+ set_option = row[0].gsub(/\s+/,'_')
116
+ user_value = row[1]
117
+ values[set_option] = user_value
118
+ values
119
+ end
120
+ end
121
+ end
122
+
123
+ def run_with_isolation_level(isolation_level)
124
+ raise ArgumentError, "Invalid isolation level, #{isolation_level}. Supported levels include #{valid_isolation_levels.to_sentence}." if !valid_isolation_levels.include?(isolation_level.upcase)
125
+ initial_isolation_level = user_options[:isolation_level] || "READ COMMITTED"
126
+ do_execute "SET TRANSACTION ISOLATION LEVEL #{isolation_level}"
127
+ begin
128
+ yield
129
+ ensure
130
+ do_execute "SET TRANSACTION ISOLATION LEVEL #{initial_isolation_level}"
131
+ end if block_given?
132
+ end
133
+
134
+ def newid_function
135
+ select_value "SELECT NEWID()"
136
+ end
137
+
138
+ def newsequentialid_function
139
+ select_value "SELECT NEWSEQUENTIALID()"
140
+ end
141
+
142
+ # === SQLServer Specific (Rake/Test Helpers) ==================== #
143
+
144
+ def recreate_database
145
+ remove_database_connections_and_rollback do
146
+ do_execute "EXEC sp_MSforeachtable 'DROP TABLE ?'"
147
+ end
148
+ end
149
+
150
+ def recreate_database!(database=nil)
151
+ current_db = current_database
152
+ database ||= current_db
153
+ this_db = database.to_s == current_db
154
+ do_execute 'USE master' if this_db
155
+ drop_database(database)
156
+ create_database(database)
157
+ ensure
158
+ use_database(current_db) if this_db
159
+ end
160
+
161
+ def drop_database(database)
162
+ retry_count = 0
163
+ max_retries = 1
164
+ begin
165
+ do_execute "DROP DATABASE #{quote_table_name(database)}"
166
+ rescue ActiveRecord::StatementInvalid => err
167
+ if err.message =~ /because it is currently in use/i
168
+ raise if retry_count >= max_retries
169
+ retry_count += 1
170
+ remove_database_connections_and_rollback(database)
171
+ retry
172
+ else
173
+ raise
174
+ end
175
+ end
176
+ end
177
+
178
+ def create_database(database)
179
+ do_execute "CREATE DATABASE #{quote_table_name(database)}"
180
+ end
181
+
182
+ def current_database
183
+ select_value 'SELECT DB_NAME()'
184
+ end
185
+
186
+ def charset
187
+ select_value "SELECT SERVERPROPERTY('SqlCharSetName')"
188
+ end
189
+
190
+
191
+ protected
192
+
193
+ def select(sql, name = nil)
194
+ raw_select sql, name, :fetch => :all
195
+ end
196
+
197
+ def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
198
+ @insert_sql = true
199
+ case @connection_options[:mode]
200
+ when :dblib
201
+ execute(sql, name) || id_value
202
+ else
203
+ super || select_value("SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident")
204
+ end
205
+ end
206
+
207
+ def update_sql(sql, name = nil)
208
+ @update_sql = true
209
+ case @connection_options[:mode]
210
+ when :dblib
211
+ execute(sql, name)
212
+ else
213
+ execute(sql, name)
214
+ select_value('SELECT @@ROWCOUNT AS AffectedRows')
215
+ end
216
+ end
217
+
218
+ # === SQLServer Specific ======================================== #
219
+
220
+ def valid_isolation_levels
221
+ ["READ COMMITTED", "READ UNCOMMITTED", "REPEATABLE READ", "SERIALIZABLE", "SNAPSHOT"]
222
+ end
223
+
224
+ # === SQLServer Specific (Executing) ============================ #
225
+
226
+ def do_execute(sql, name = nil)
227
+ name ||= 'EXECUTE'
228
+ log(sql, name) do
229
+ with_auto_reconnect { raw_connection_do(sql) }
230
+ end
231
+ end
232
+
233
+ def raw_connection_do(sql)
234
+ case @connection_options[:mode]
235
+ when :dblib
236
+ @insert_sql ? @connection.execute(sql).insert : @connection.execute(sql).do
237
+ when :odbc
238
+ @connection.do(sql)
239
+ else :adonet
240
+ @connection.create_command.tap{ |cmd| cmd.command_text = sql }.execute_non_query
241
+ end
242
+ ensure
243
+ @insert_sql = false
244
+ @update_sql = false
245
+ end
246
+
247
+ # === SQLServer Specific (Selecting) ============================ #
248
+
249
+ def raw_select(sql, name=nil, options={})
250
+ log(sql,name) do
251
+ begin
252
+ handle = raw_connection_run(sql)
253
+ handle_to_names_and_values(handle, options)
254
+ ensure
255
+ finish_statement_handle(handle)
256
+ end
257
+ end
258
+ end
259
+
260
+ def raw_connection_run(sql)
261
+ with_auto_reconnect do
262
+ case @connection_options[:mode]
263
+ when :dblib
264
+ @connection.execute(sql)
265
+ when :odbc
266
+ block_given? ? @connection.run_block(sql) { |handle| yield(handle) } : @connection.run(sql)
267
+ else :adonet
268
+ @connection.create_command.tap{ |cmd| cmd.command_text = sql }.execute_reader
269
+ end
270
+ end
271
+ end
272
+
273
+ def handle_more_results?(handle)
274
+ case @connection_options[:mode]
275
+ when :dblib
276
+ when :odbc
277
+ handle.more_results
278
+ when :adonet
279
+ handle.next_result
280
+ end
281
+ end
282
+
283
+ def handle_to_names_and_values(handle, options={})
284
+ case @connection_options[:mode]
285
+ when :dblib
286
+ handle_to_names_and_values_dblib(handle, options)
287
+ when :odbc
288
+ handle_to_names_and_values_odbc(handle, options)
289
+ when :adonet
290
+ handle_to_names_and_values_adonet(handle, options)
291
+ end
292
+ end
293
+
294
+ def handle_to_names_and_values_dblib(handle, options={})
295
+ query_options = {}.tap do |qo|
296
+ qo[:timezone] = ActiveRecord::Base.default_timezone || :utc
297
+ qo[:first] = true if options[:fetch] == :one
298
+ qo[:as] = options[:fetch] == :rows ? :array : :hash
299
+ end
300
+ handle.each(query_options)
301
+ end
302
+
303
+ def handle_to_names_and_values_odbc(handle, options={})
304
+ @connection.use_utc = ActiveRecord::Base.default_timezone == :utc if @connection_supports_native_types
305
+ case options[:fetch]
306
+ when :all, :one
307
+ if @connection_supports_native_types
308
+ if options[:fetch] == :all
309
+ handle.each_hash || []
310
+ else
311
+ row = handle.fetch_hash
312
+ rows = row ? [row] : [[]]
313
+ end
314
+ else
315
+ rows = if options[:fetch] == :all
316
+ handle.fetch_all || []
317
+ else
318
+ row = handle.fetch
319
+ row ? [row] : [[]]
320
+ end
321
+ names = handle.columns(true).map{ |c| c.name }
322
+ names_and_values = []
323
+ rows.each do |row|
324
+ h = {}
325
+ i = 0
326
+ while i < row.size
327
+ v = row[i]
328
+ h[names[i]] = v.respond_to?(:to_sqlserver_string) ? v.to_sqlserver_string : v
329
+ i += 1
330
+ end
331
+ names_and_values << h
332
+ end
333
+ names_and_values
334
+ end
335
+ when :rows
336
+ rows = handle.fetch_all || []
337
+ return rows if @connection_supports_native_types
338
+ rows.each do |row|
339
+ i = 0
340
+ while i < row.size
341
+ v = row[i]
342
+ row[i] = v.to_sqlserver_string if v.respond_to?(:to_sqlserver_string)
343
+ i += 1
344
+ end
345
+ end
346
+ rows
347
+ end
348
+ end
349
+
350
+ def handle_to_names_and_values_adonet(handle, options={})
351
+ if handle.has_rows
352
+ names = []
353
+ rows = []
354
+ fields_named = options[:fetch] == :rows
355
+ one_row_only = options[:fetch] == :one
356
+ while handle.read
357
+ row = []
358
+ handle.visible_field_count.times do |row_index|
359
+ value = handle.get_value(row_index)
360
+ value = case value
361
+ when System::String
362
+ value.to_s
363
+ when System::DBNull
364
+ nil
365
+ when System::DateTime
366
+ value.to_string("yyyy-MM-dd HH:mm:ss.fff").to_s
367
+ when @@array_of_bytes ||= System::Array[System::Byte]
368
+ String.new(value)
369
+ else
370
+ value
371
+ end
372
+ row << value
373
+ names << handle.get_name(row_index).to_s unless fields_named
374
+ break if one_row_only
375
+ end
376
+ rows << row
377
+ fields_named = true
378
+ end
379
+ else
380
+ rows = []
381
+ end
382
+ if options[:fetch] != :rows
383
+ names_and_values = []
384
+ rows.each do |row|
385
+ h = {}
386
+ i = 0
387
+ while i < row.size
388
+ h[names[i]] = row[i]
389
+ i += 1
390
+ end
391
+ names_and_values << h
392
+ end
393
+ names_and_values
394
+ else
395
+ rows
396
+ end
397
+ end
398
+
399
+ def finish_statement_handle(handle)
400
+ case @connection_options[:mode]
401
+ when :dblib
402
+ when :odbc
403
+ handle.drop if handle && handle.respond_to?(:drop) && !handle.finished?
404
+ when :adonet
405
+ handle.close if handle && handle.respond_to?(:close) && !handle.is_closed
406
+ handle.dispose if handle && handle.respond_to?(:dispose)
407
+ end
408
+ handle
409
+ end
410
+
411
+ end
412
+ end
413
+ end
414
+ end