activerecord-sqlserver-adapter-2000 3.0.15

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,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