rails-sqlserver-2000-2005-adapter 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,71 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module SQLServerActiveRecordExtensions
4
+
5
+ def self.included(klass)
6
+ klass.extend ClassMethods
7
+ class << klass
8
+ alias_method_chain :reset_column_information, :sqlserver_columns_cache_support
9
+ alias_method_chain :add_order!, :sqlserver_unique_checking
10
+ end
11
+ end
12
+
13
+ module ClassMethods
14
+
15
+ def coerce_sqlserver_date(*attributes)
16
+ write_inheritable_attribute :coerced_sqlserver_date_columns, Set.new(attributes.map(&:to_s))
17
+ end
18
+
19
+ def coerce_sqlserver_time(*attributes)
20
+ write_inheritable_attribute :coerced_sqlserver_time_columns, Set.new(attributes.map(&:to_s))
21
+ end
22
+
23
+ def coerced_sqlserver_date_columns
24
+ read_inheritable_attribute(:coerced_sqlserver_date_columns) || []
25
+ end
26
+
27
+ def coerced_sqlserver_time_columns
28
+ read_inheritable_attribute(:coerced_sqlserver_time_columns) || []
29
+ end
30
+
31
+ def reset_column_information_with_sqlserver_columns_cache_support
32
+ connection.instance_variable_set :@sqlserver_columns_cache, {}
33
+ reset_column_information_without_sqlserver_columns_cache_support
34
+ end
35
+
36
+ private
37
+
38
+ def add_order_with_sqlserver_unique_checking!(sql, order, scope = :auto)
39
+ order_sql = ''
40
+ add_order_without_sqlserver_unique_checking!(order_sql, order, scope)
41
+ unless order_sql.blank?
42
+ unique_order_hash = {}
43
+ select_table_name = connection.send(:get_table_name,sql)
44
+ select_table_name.tr!('[]','') if select_table_name
45
+ orders_and_dirs_set = connection.send(:orders_and_dirs_set,order_sql)
46
+ unique_order_sql = orders_and_dirs_set.inject([]) do |array,order_dir|
47
+ ord, dir = order_dir
48
+ ord_tn_and_cn = ord.to_s.split('.').map{|o|o.tr('[]','')}
49
+ ord_table_name, ord_column_name = if ord_tn_and_cn.size > 1
50
+ ord_tn_and_cn
51
+ else
52
+ [nil, ord_tn_and_cn.first]
53
+ end
54
+ if (ord_table_name && ord_table_name == select_table_name && unique_order_hash[ord_column_name]) || unique_order_hash[ord_column_name]
55
+ array
56
+ else
57
+ unique_order_hash[ord_column_name] = true
58
+ array << "#{ord} #{dir}".strip
59
+ end
60
+ end.join(', ')
61
+ sql << " ORDER BY #{unique_order_sql}"
62
+ end
63
+ end
64
+
65
+ end
66
+
67
+ end
68
+ end
69
+ end
70
+
71
+ ActiveRecord::Base.send :include, ActiveRecord::ConnectionAdapters::SQLServerActiveRecordExtensions
@@ -0,0 +1,83 @@
1
+
2
+ module SQLServerDBI
3
+
4
+ module Timestamp
5
+ # Will further change DBI::Timestamp #to_s return value by limiting the usec of
6
+ # the time to 3 digits and in some cases adding zeros if needed. For example:
7
+ # "1985-04-15 00:00:00.0" # => "1985-04-15 00:00:00.000"
8
+ # "2008-11-08 10:24:36.547000" # => "2008-11-08 10:24:36.547"
9
+ # "2008-11-08 10:24:36.123" # => "2008-11-08 10:24:36.000"
10
+ def to_sqlserver_string
11
+ datetime, usec = to_s[0..22].split('.')
12
+ "#{datetime}.#{sprintf("%03d",usec)}"
13
+ end
14
+ end
15
+
16
+
17
+ module Type
18
+
19
+ # Make sure we get DBI::Type::Timestamp returning a string NOT a time object
20
+ # that represents what is in the DB before type casting and let the adapter
21
+ # do the reset. DBI::DBD::ODBC will typically return a string like:
22
+ # "1985-04-15 00:00:00 0" # => "1985-04-15 00:00:00.000"
23
+ # "2008-11-08 10:24:36 547000000" # => "2008-11-08 10:24:36.547"
24
+ # "2008-11-08 10:24:36 123000000" # => "2008-11-08 10:24:36.000"
25
+ class SqlserverTimestamp
26
+ def self.parse(obj)
27
+ return nil if ::DBI::Type::Null.parse(obj).nil?
28
+ date, time, fraction = obj.split(' ')
29
+ "#{date} #{time}.#{sprintf("%03d",fraction)}"
30
+ end
31
+ end
32
+
33
+ # The adapter and rails will parse our floats, decimals, and money field correctly
34
+ # from a string. Do not let the DBI::Type classes create Float/BigDecimal objects
35
+ # for us. Trust rails .type_cast to do what it is built to do.
36
+ class SqlserverForcedString
37
+ def self.parse(obj)
38
+ return nil if ::DBI::Type::Null.parse(obj).nil?
39
+ obj.to_s
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+ module TypeUtil
46
+
47
+ def self.included(klass)
48
+ klass.extend ClassMethods
49
+ class << klass
50
+ alias_method_chain :type_name_to_module, :sqlserver_types
51
+ end
52
+ end
53
+
54
+ module ClassMethods
55
+
56
+ # Capture all types classes that we need to handle directly for SQL Server
57
+ # and allow normal processing for those that we do not.
58
+ def type_name_to_module_with_sqlserver_types(type_name)
59
+ case type_name
60
+ when /^timestamp$/i
61
+ DBI::Type::SqlserverTimestamp
62
+ when /^float|decimal|money$/i
63
+ DBI::Type::SqlserverForcedString
64
+ else
65
+ type_name_to_module_without_sqlserver_types(type_name)
66
+ end
67
+ end
68
+
69
+ end
70
+
71
+ end
72
+
73
+
74
+ end
75
+
76
+
77
+ if defined?(DBI::TypeUtil)
78
+ DBI::Type.send :include, SQLServerDBI::Type
79
+ DBI::TypeUtil.send :include, SQLServerDBI::TypeUtil
80
+ elsif defined?(DBI::Timestamp) # DEPRECATED in DBI 0.4.0 and above. Remove when 0.2.2 and lower is no longer supported.
81
+ DBI::Timestamp.send :include, SQLServerDBI::Timestamp
82
+ end
83
+
@@ -0,0 +1,19 @@
1
+ # The filename begins with "aaaa" to ensure this is the first test.
2
+ require 'cases/sqlserver_helper'
3
+
4
+ class AAAACreateTablesTestSqlserver < ActiveRecord::TestCase
5
+ self.use_transactional_fixtures = false
6
+
7
+ should 'load activerecord schema' do
8
+ schema_file = "#{ACTIVERECORD_TEST_ROOT}/schema/schema.rb"
9
+ eval(File.read(schema_file))
10
+ assert true
11
+ end
12
+
13
+ should 'load sqlserver specific schema' do
14
+ sqlserver_specific_schema_file = "#{SQLSERVER_SCHEMA_ROOT}/sqlserver_specific_schema.rb"
15
+ eval(File.read(sqlserver_specific_schema_file))
16
+ assert true
17
+ end
18
+
19
+ end
@@ -0,0 +1,428 @@
1
+ require 'cases/sqlserver_helper'
2
+ require 'models/task'
3
+ require 'models/reply'
4
+ require 'models/joke'
5
+ require 'models/subscriber'
6
+
7
+ class AdapterTestSqlserver < ActiveRecord::TestCase
8
+
9
+ def setup
10
+ @connection = ActiveRecord::Base.connection
11
+ @basic_insert_sql = "INSERT INTO [funny_jokes] ([name]) VALUES('Knock knock')"
12
+ @basic_update_sql = "UPDATE [customers] SET [address_street] = NULL WHERE [id] = 2"
13
+ @basic_select_sql = "SELECT * FROM [customers] WHERE ([customers].[id] = 1)"
14
+ end
15
+
16
+ context 'For abstract behavior' do
17
+
18
+ should 'have a 128 max #table_alias_length' do
19
+ assert @connection.table_alias_length <= 128
20
+ end
21
+
22
+ should 'raise invalid statement error' do
23
+ assert_raise(ActiveRecord::StatementInvalid) { Topic.connection.update("UPDATE XXX") }
24
+ end
25
+
26
+ should 'be our adapter_name' do
27
+ assert_equal 'SQLServer', @connection.adapter_name
28
+ end
29
+
30
+ should 'support migrations' do
31
+ assert @connection.supports_migrations?
32
+ end
33
+
34
+ should 'support DDL in transactions' do
35
+ assert @connection.supports_ddl_transactions?
36
+ end
37
+
38
+ context 'for database version' do
39
+
40
+ setup do
41
+ @version_regexp = ActiveRecord::ConnectionAdapters::SQLServerAdapter::DATABASE_VERSION_REGEXP
42
+ @supported_version = ActiveRecord::ConnectionAdapters::SQLServerAdapter::SUPPORTED_VERSIONS
43
+ @sqlserver_2000_string = "Microsoft SQL Server 2000 - 8.00.2039 (Intel X86)"
44
+ @sqlserver_2005_string = "Microsoft SQL Server 2005 - 9.00.3215.00 (Intel X86)"
45
+ end
46
+
47
+ should 'return a string from #database_version that matches class regexp' do
48
+ assert_match @version_regexp, @connection.database_version
49
+ end
50
+
51
+ should 'return a 4 digit year fixnum for #database_year' do
52
+ assert_instance_of Fixnum, @connection.database_year
53
+ assert_contains @supported_version, @connection.database_year
54
+ end
55
+
56
+ should 'return true to #sqlserver_2000?' do
57
+ @connection.stubs(:database_version).returns(@sqlserver_2000_string)
58
+ assert @connection.sqlserver_2000?
59
+ end
60
+
61
+ should 'return true to #sqlserver_2005?' do
62
+ @connection.stubs(:database_version).returns(@sqlserver_2005_string)
63
+ assert @connection.sqlserver_2005?
64
+ end
65
+
66
+ end
67
+
68
+ context 'for #unqualify_table_name and #unqualify_db_name' do
69
+
70
+ setup do
71
+ @expected_table_name = 'baz'
72
+ @expected_db_name = 'foo'
73
+ @first_second_table_names = ['[baz]','baz','[bar].[baz]','bar.baz']
74
+ @third_table_names = ['[foo].[bar].[baz]','foo.bar.baz']
75
+ @qualifed_table_names = @first_second_table_names + @third_table_names
76
+ end
77
+
78
+ should 'return clean table_name from #unqualify_table_name' do
79
+ @qualifed_table_names.each do |qtn|
80
+ assert_equal @expected_table_name,
81
+ @connection.send(:unqualify_table_name,qtn),
82
+ "This qualifed_table_name #{qtn} did not unqualify correctly."
83
+ end
84
+ end
85
+
86
+ should 'return nil from #unqualify_db_name when table_name is less than 2 qualified' do
87
+ @first_second_table_names.each do |qtn|
88
+ assert_equal nil, @connection.send(:unqualify_db_name,qtn),
89
+ "This qualifed_table_name #{qtn} did not return nil."
90
+ end
91
+ end
92
+
93
+ should 'return clean db_name from #unqualify_db_name when table is thrid level qualified' do
94
+ @third_table_names.each do |qtn|
95
+ assert_equal @expected_db_name,
96
+ @connection.send(:unqualify_db_name,qtn),
97
+ "This qualifed_table_name #{qtn} did not unqualify the db_name correctly."
98
+ end
99
+ end
100
+
101
+ end
102
+
103
+ should 'return true to #insert_sql? for inserts only' do
104
+ assert @connection.send(:insert_sql?,'INSERT...')
105
+ assert !@connection.send(:insert_sql?,'UPDATE...')
106
+ assert !@connection.send(:insert_sql?,'SELECT...')
107
+ end
108
+
109
+ context 'for #sql_for_association_limiting?' do
110
+
111
+ should 'return false for simple selects with no GROUP BY and ORDER BY' do
112
+ assert !sql_for_association_limiting?("SELECT * FROM [posts]")
113
+ end
114
+
115
+ should 'return true to single SELECT, ideally a table/primarykey, that also has a GROUP BY and ORDER BY' do
116
+ assert sql_for_association_limiting?("SELECT [posts].id FROM...GROUP BY [posts].id ORDER BY MIN(posts.id)")
117
+ end
118
+
119
+ should 'return false to single * wildcard SELECT that also has a GROUP BY and ORDER BY' do
120
+ assert !sql_for_association_limiting?("SELECT * FROM...GROUP BY [posts].id ORDER BY MIN(posts.id)")
121
+ end
122
+
123
+ should 'return false to multiple columns in the select even when GROUP BY and ORDER BY are present' do
124
+ sql = "SELECT [accounts].credit_limit, firm_id FROM...GROUP BY firm_id ORDER BY firm_id"
125
+ assert !sql_for_association_limiting?(sql)
126
+ end
127
+
128
+ end
129
+
130
+ context 'for #get_table_name' do
131
+
132
+ should 'return quoted table name from basic INSERT, UPDATE and SELECT statements' do
133
+ assert_equal '[funny_jokes]', @connection.send(:get_table_name,@basic_insert_sql)
134
+ assert_equal '[customers]', @connection.send(:get_table_name,@basic_update_sql)
135
+ assert_equal '[customers]', @connection.send(:get_table_name,@basic_select_sql)
136
+ end
137
+
138
+ end
139
+
140
+ context 'dealing with various orders SQL snippets' do
141
+
142
+ setup do
143
+ @single_order = 'comments.id'
144
+ @single_order_with_desc = 'comments.id DESC'
145
+ @two_orders = 'comments.id, comments.post_id'
146
+ @two_orders_with_asc = 'comments.id, comments.post_id ASC'
147
+ @two_orders_with_desc_and_asc = 'comments.id DESC, comments.post_id ASC'
148
+ @two_duplicate_order_with_dif_dir = "id, id DESC"
149
+ end
150
+
151
+ should 'convert to an 2D array of column/direction arrays using #orders_and_dirs_set' do
152
+ assert_equal [['comments.id',nil]], orders_and_dirs_set('ORDER BY comments.id'), 'Needs to remove ORDER BY'
153
+ assert_equal [['comments.id',nil]], orders_and_dirs_set(@single_order)
154
+ assert_equal [['comments.id',nil],['comments.post_id',nil]], orders_and_dirs_set(@two_orders)
155
+ assert_equal [['comments.id',nil],['comments.post_id','ASC']], orders_and_dirs_set(@two_orders_with_asc)
156
+ assert_equal [['id',nil],['id','DESC']], orders_and_dirs_set(@two_duplicate_order_with_dif_dir)
157
+ end
158
+
159
+ should 'remove duplicate or maintain the same order by statements giving precedence to first using #add_order! method chain extension' do
160
+ assert_equal ' ORDER BY comments.id', add_order!(@single_order)
161
+ assert_equal ' ORDER BY comments.id DESC', add_order!(@single_order_with_desc)
162
+ assert_equal ' ORDER BY comments.id, comments.post_id', add_order!(@two_orders)
163
+ assert_equal ' ORDER BY comments.id DESC, comments.post_id ASC', add_order!(@two_orders_with_desc_and_asc)
164
+ assert_equal 'SELECT * FROM [developers] ORDER BY id', add_order!('id, developers.id DESC','SELECT * FROM [developers]')
165
+ assert_equal 'SELECT * FROM [developers] ORDER BY [developers].[id] DESC', add_order!('[developers].[id] DESC, id','SELECT * FROM [developers]')
166
+ end
167
+
168
+ should 'take all types of order options and convert them to MIN functions using #order_to_min_set' do
169
+ assert_equal 'MIN(comments.id)', order_to_min_set(@single_order)
170
+ assert_equal 'MIN(comments.id), MIN(comments.post_id)', order_to_min_set(@two_orders)
171
+ assert_equal 'MIN(comments.id) DESC', order_to_min_set(@single_order_with_desc)
172
+ assert_equal 'MIN(comments.id), MIN(comments.post_id) ASC', order_to_min_set(@two_orders_with_asc)
173
+ assert_equal 'MIN(comments.id) DESC, MIN(comments.post_id) ASC', order_to_min_set(@two_orders_with_desc_and_asc)
174
+ end
175
+
176
+ end
177
+
178
+ context 'with different language' do
179
+
180
+ teardown do
181
+ @connection.execute("SET LANGUAGE us_english") rescue nil
182
+ end
183
+
184
+ should_eventually 'do a date insertion when language is german' do
185
+ @connection.execute("SET LANGUAGE deutsch")
186
+ assert_nothing_raised do
187
+ Task.create(:starting => Time.utc(2000, 1, 31, 5, 42, 0), :ending => Date.new(2006, 12, 31))
188
+ end
189
+ end
190
+
191
+ end
192
+
193
+ end
194
+
195
+ context 'For chronic data types' do
196
+
197
+ context 'with a usec' do
198
+
199
+ setup do
200
+ @time = Time.now
201
+ end
202
+
203
+ should 'truncate 123456 usec to just 123' do
204
+ @time.stubs(:usec).returns(123456)
205
+ saved = SqlServerChronic.create!(:datetime => @time).reload
206
+ assert_equal 123000, saved.datetime.usec
207
+ end
208
+
209
+ should 'drop 123 to 0' do
210
+ @time.stubs(:usec).returns(123)
211
+ saved = SqlServerChronic.create!(:datetime => @time).reload
212
+ assert_equal 0, saved.datetime.usec
213
+ assert_equal '000', saved.datetime_before_type_cast.split('.').last
214
+ end
215
+
216
+ end
217
+
218
+ context 'which have coerced types' do
219
+
220
+ setup do
221
+ christmas_08 = "2008-12-25".to_time
222
+ christmas_08_afternoon = "2008-12-25 12:00".to_time
223
+ @chronic_date = SqlServerChronic.create!(:date => christmas_08).reload
224
+ @chronic_time = SqlServerChronic.create!(:time => christmas_08_afternoon).reload
225
+ end
226
+
227
+ should 'have an inheritable attribute ' do
228
+ assert SqlServerChronic.coerced_sqlserver_date_columns.include?('date')
229
+ end
230
+
231
+ should 'have column and objects cast to date' do
232
+ date_column = SqlServerChronic.columns_hash['date']
233
+ assert_equal :date, date_column.type, "This column: \n#{date_column.inspect}"
234
+ assert_instance_of Date, @chronic_date.date
235
+ end
236
+
237
+ should 'have column objects cast to time' do
238
+ time_column = SqlServerChronic.columns_hash['time']
239
+ assert_equal :time, time_column.type, "This column: \n#{time_column.inspect}"
240
+ assert_instance_of Time, @chronic_time.time
241
+ end
242
+
243
+ end
244
+
245
+ end
246
+
247
+ context 'For identity inserts' do
248
+
249
+ setup do
250
+ @identity_insert_sql = "INSERT INTO [funny_jokes] ([id],[name]) VALUES(420,'Knock knock')"
251
+ end
252
+
253
+ should 'return quoted table_name to #query_requires_identity_insert? when INSERT sql contains id_column' do
254
+ assert_equal '[funny_jokes]', @connection.send(:query_requires_identity_insert?,@identity_insert_sql)
255
+ end
256
+
257
+ should 'return false to #query_requires_identity_insert? for normal SQL' do
258
+ [@basic_insert_sql, @basic_update_sql, @basic_select_sql].each do |sql|
259
+ assert !@connection.send(:query_requires_identity_insert?,sql), "SQL was #{sql}"
260
+ end
261
+ end
262
+
263
+ should 'find identity column using #identity_column' do
264
+ joke_id_column = Joke.columns.detect { |c| c.name == 'id' }
265
+ assert_equal joke_id_column, @connection.send(:identity_column,Joke.table_name)
266
+ end
267
+
268
+ should 'return nil when calling #identity_column for a table_name with no identity' do
269
+ assert_nil @connection.send(:identity_column,Subscriber.table_name)
270
+ end
271
+
272
+ end
273
+
274
+ context 'For Quoting' do
275
+
276
+ should 'return 1 for #quoted_true' do
277
+ assert_equal '1', @connection.quoted_true
278
+ end
279
+
280
+ should 'return 0 for #quoted_false' do
281
+ assert_equal '0', @connection.quoted_false
282
+ end
283
+
284
+ should 'not escape backslash characters like abstract adapter' do
285
+ string_with_backslashs = "\\n"
286
+ assert_equal string_with_backslashs, @connection.quote_string(string_with_backslashs)
287
+ end
288
+
289
+ should 'quote column names with brackets' do
290
+ assert_equal '[foo]', @connection.quote_column_name(:foo)
291
+ assert_equal '[foo]', @connection.quote_column_name('foo')
292
+ assert_equal '[foo].[bar]', @connection.quote_column_name('foo.bar')
293
+ end
294
+
295
+ should 'quote table names like columns' do
296
+ assert_equal '[foo].[bar]', @connection.quote_column_name('foo.bar')
297
+ assert_equal '[foo].[bar].[baz]', @connection.quote_column_name('foo.bar.baz')
298
+ end
299
+
300
+ end
301
+
302
+ context 'When disableing referential integrity' do
303
+
304
+ setup do
305
+ @parent = FkTestHasPk.create!
306
+ @member = FkTestHasFk.create!(:fk_id => @parent.id)
307
+ end
308
+
309
+ should 'NOT ALLOW by default the deletion of a referenced parent' do
310
+ assert_raise(ActiveRecord::StatementInvalid) { @parent.destroy }
311
+ end
312
+
313
+ should 'ALLOW deletion of referenced parent using #disable_referential_integrity block' do
314
+ assert_nothing_raised(ActiveRecord::StatementInvalid) do
315
+ FkTestHasPk.connection.disable_referential_integrity { @parent.destroy }
316
+ end
317
+ end
318
+
319
+ should 'again NOT ALLOW deletion of referenced parent after #disable_referential_integrity block' do
320
+ assert_raise(ActiveRecord::StatementInvalid) do
321
+ FkTestHasPk.connection.disable_referential_integrity { }
322
+ @parent.destroy
323
+ end
324
+ end
325
+
326
+ end
327
+
328
+ context 'For DatabaseStatements' do
329
+
330
+ end
331
+
332
+ context 'For SchemaStatements' do
333
+
334
+ context 'returning from #type_to_sql' do
335
+
336
+ should 'create integers when no limit supplied' do
337
+ assert_equal 'integer', @connection.type_to_sql(:integer)
338
+ end
339
+
340
+ should 'create integers when limit is 4' do
341
+ assert_equal 'integer', @connection.type_to_sql(:integer, 4)
342
+ end
343
+
344
+ should 'create integers when limit is 3' do
345
+ assert_equal 'integer', @connection.type_to_sql(:integer, 3)
346
+ end
347
+
348
+ should 'create smallints when limit is less than 3' do
349
+ assert_equal 'smallint', @connection.type_to_sql(:integer, 2)
350
+ assert_equal 'smallint', @connection.type_to_sql(:integer, 1)
351
+ end
352
+
353
+ should 'create bigints when limit is greateer than 4' do
354
+ assert_equal 'bigint', @connection.type_to_sql(:integer, 5)
355
+ assert_equal 'bigint', @connection.type_to_sql(:integer, 6)
356
+ assert_equal 'bigint', @connection.type_to_sql(:integer, 7)
357
+ assert_equal 'bigint', @connection.type_to_sql(:integer, 8)
358
+ end
359
+
360
+ end
361
+
362
+ end
363
+
364
+ context 'For indexes' do
365
+
366
+ setup do
367
+ @desc_index_name = 'idx_credit_limit_test_desc'
368
+ @connection.execute "CREATE INDEX #{@desc_index_name} ON accounts (credit_limit DESC)"
369
+ end
370
+
371
+ teardown do
372
+ @connection.execute "DROP INDEX accounts.#{@desc_index_name}"
373
+ end
374
+
375
+ should 'have indexes with descending order' do
376
+ assert @connection.indexes('accounts').detect { |i| i.name == @desc_index_name }
377
+ end
378
+
379
+ end
380
+
381
+
382
+ private
383
+
384
+ def sql_for_association_limiting?(sql)
385
+ @connection.send :sql_for_association_limiting?, sql
386
+ end
387
+
388
+ def orders_and_dirs_set(order)
389
+ @connection.send :orders_and_dirs_set, order
390
+ end
391
+
392
+ def add_order!(order,sql='')
393
+ ActiveRecord::Base.send :add_order!, sql, order, nil
394
+ sql
395
+ end
396
+
397
+ def order_to_min_set(order)
398
+ @connection.send :order_to_min_set, order
399
+ end
400
+
401
+ end
402
+
403
+
404
+ class AdapterTest < ActiveRecord::TestCase
405
+
406
+ COERCED_TESTS = [
407
+ :test_add_limit_offset_should_sanitize_sql_injection_for_limit_without_comas,
408
+ :test_add_limit_offset_should_sanitize_sql_injection_for_limit_with_comas
409
+ ]
410
+
411
+ include SqlserverCoercedTest
412
+
413
+ def test_coerced_test_add_limit_offset_should_sanitize_sql_injection_for_limit_without_comas
414
+ sql_inject = "1 select * from schema"
415
+ connection = ActiveRecord::Base.connection
416
+ assert_raise(ArgumentError) { connection.add_limit_offset!("", :limit=>sql_inject) }
417
+ assert_raise(ArgumentError) { connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7) }
418
+ end
419
+
420
+ def test_coerced_test_add_limit_offset_should_sanitize_sql_injection_for_limit_with_comas
421
+ sql_inject = "1, 7 procedure help()"
422
+ connection = ActiveRecord::Base.connection
423
+ assert_raise(ArgumentError) { connection.add_limit_offset!("", :limit=>sql_inject) }
424
+ assert_raise(ArgumentError) { connection.add_limit_offset!("", :limit=> '1 ; DROP TABLE USERS', :offset=>7) }
425
+ assert_raise(ArgumentError) { connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7) }
426
+ end
427
+
428
+ end
@@ -0,0 +1,21 @@
1
+ require 'cases/sqlserver_helper'
2
+ require 'models/developer'
3
+
4
+ class BasicsTestSqlserver < ActiveRecord::TestCase
5
+ end
6
+
7
+ class BasicsTest < ActiveRecord::TestCase
8
+
9
+ COERCED_TESTS = [:test_read_attributes_before_type_cast_on_datetime]
10
+
11
+ include SqlserverCoercedTest
12
+
13
+ fixtures :developers
14
+
15
+ def test_coerced_test_read_attributes_before_type_cast_on_datetime
16
+ developer = Developer.find(:first)
17
+ assert_equal developer.created_at.to_s(:db)+'.000' , developer.attributes_before_type_cast["created_at"]
18
+ end
19
+
20
+
21
+ end
@@ -0,0 +1,20 @@
1
+ require 'cases/sqlserver_helper'
2
+ require 'models/company'
3
+
4
+ class CalculationsTestSqlserver < ActiveRecord::TestCase
5
+ end
6
+
7
+ class CalculationsTest < ActiveRecord::TestCase
8
+
9
+ COERCED_TESTS = [:test_should_sum_expression]
10
+
11
+ include SqlserverCoercedTest
12
+
13
+ fixtures :accounts
14
+
15
+ def test_coerced_test_should_sum_expression
16
+ assert_equal 636, Account.sum("2 * credit_limit")
17
+ end
18
+
19
+
20
+ end