activerecord-sqlserver-adapter 2.3.7 → 3.2.18

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.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +385 -61
  3. data/MIT-LICENSE +1 -1
  4. data/VERSION +1 -0
  5. data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +42 -0
  6. data/lib/active_record/connection_adapters/sqlserver/core_ext/database_statements.rb +97 -0
  7. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +41 -0
  8. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +26 -0
  9. data/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb +38 -0
  10. data/lib/active_record/connection_adapters/sqlserver/core_ext/relation.rb +19 -0
  11. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +49 -0
  12. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +458 -0
  13. data/lib/active_record/connection_adapters/sqlserver/errors.rb +36 -0
  14. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +113 -0
  15. data/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +85 -0
  16. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +376 -0
  17. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +69 -0
  18. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +25 -0
  19. data/lib/active_record/connection_adapters/sqlserver/showplan.rb +67 -0
  20. data/lib/active_record/connection_adapters/sqlserver/utils.rb +32 -0
  21. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +344 -1055
  22. data/lib/arel/visitors/sqlserver.rb +389 -0
  23. metadata +60 -83
  24. data/README.rdoc +0 -190
  25. data/RUNNING_UNIT_TESTS +0 -65
  26. data/Rakefile +0 -41
  27. data/autotest/discover.rb +0 -4
  28. data/autotest/railssqlserver.rb +0 -16
  29. data/autotest/sqlserver.rb +0 -54
  30. data/lib/active_record/connection_adapters/sqlserver_adapter/core_ext/active_record.rb +0 -151
  31. data/lib/active_record/connection_adapters/sqlserver_adapter/core_ext/odbc.rb +0 -40
  32. data/test/cases/aaaa_create_tables_test_sqlserver.rb +0 -19
  33. data/test/cases/adapter_test_sqlserver.rb +0 -756
  34. data/test/cases/attribute_methods_test_sqlserver.rb +0 -33
  35. data/test/cases/basics_test_sqlserver.rb +0 -21
  36. data/test/cases/calculations_test_sqlserver.rb +0 -20
  37. data/test/cases/column_test_sqlserver.rb +0 -285
  38. data/test/cases/connection_test_sqlserver.rb +0 -146
  39. data/test/cases/eager_association_test_sqlserver.rb +0 -42
  40. data/test/cases/execute_procedure_test_sqlserver.rb +0 -44
  41. data/test/cases/inheritance_test_sqlserver.rb +0 -28
  42. data/test/cases/method_scoping_test_sqlserver.rb +0 -28
  43. data/test/cases/migration_test_sqlserver.rb +0 -123
  44. data/test/cases/named_scope_test_sqlserver.rb +0 -21
  45. data/test/cases/offset_and_limit_test_sqlserver.rb +0 -108
  46. data/test/cases/pessimistic_locking_test_sqlserver.rb +0 -125
  47. data/test/cases/query_cache_test_sqlserver.rb +0 -24
  48. data/test/cases/schema_dumper_test_sqlserver.rb +0 -72
  49. data/test/cases/specific_schema_test_sqlserver.rb +0 -97
  50. data/test/cases/sqlserver_helper.rb +0 -127
  51. data/test/cases/table_name_test_sqlserver.rb +0 -38
  52. data/test/cases/transaction_test_sqlserver.rb +0 -93
  53. data/test/cases/unicode_test_sqlserver.rb +0 -50
  54. data/test/cases/validations_test_sqlserver.rb +0 -35
  55. data/test/connections/native_sqlserver/connection.rb +0 -25
  56. data/test/connections/native_sqlserver_odbc/connection.rb +0 -27
  57. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +0 -11
  58. data/test/schema/sqlserver_specific_schema.rb +0 -94
@@ -1,756 +0,0 @@
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
- fixtures :tasks
10
-
11
- def setup
12
- @connection = ActiveRecord::Base.connection
13
- @basic_insert_sql = "INSERT INTO [funny_jokes] ([name]) VALUES('Knock knock')"
14
- @basic_update_sql = "UPDATE [customers] SET [address_street] = NULL WHERE [id] = 2"
15
- @basic_select_sql = "SELECT * FROM [customers] WHERE ([customers].[id] = 1)"
16
- end
17
-
18
- context 'For abstract behavior' do
19
-
20
- should 'have a 128 max #table_alias_length' do
21
- assert @connection.table_alias_length <= 128
22
- end
23
-
24
- should 'raise invalid statement error' do
25
- assert_raise(ActiveRecord::StatementInvalid) { Topic.connection.update("UPDATE XXX") }
26
- end
27
-
28
- should 'be our adapter_name' do
29
- assert_equal 'SQLServer', @connection.adapter_name
30
- end
31
-
32
- should 'include version in inspect' do
33
- assert_match(/version\: \d.\d/,@connection.inspect)
34
- end
35
-
36
- should 'support migrations' do
37
- assert @connection.supports_migrations?
38
- end
39
-
40
- should 'support DDL in transactions' do
41
- assert @connection.supports_ddl_transactions?
42
- end
43
-
44
- should 'allow owner table name prefixs like dbo. to still allow table_exists? to return true' do
45
- begin
46
- assert_equal 'tasks', Task.table_name
47
- assert Task.table_exists?
48
- Task.table_name = 'dbo.tasks'
49
- assert Task.table_exists?, 'Tasks table name of dbo.tasks should return true for exists.'
50
- ensure
51
- Task.table_name = 'tasks'
52
- end
53
- end
54
-
55
- context 'for database version' do
56
-
57
- setup do
58
- @version_regexp = ActiveRecord::ConnectionAdapters::SQLServerAdapter::DATABASE_VERSION_REGEXP
59
- @supported_version = ActiveRecord::ConnectionAdapters::SQLServerAdapter::SUPPORTED_VERSIONS
60
- @sqlserver_2000_string = "Microsoft SQL Server 2000 - 8.00.2039 (Intel X86)"
61
- @sqlserver_2005_string = "Microsoft SQL Server 2005 - 9.00.3215.00 (Intel X86)"
62
- @sqlserver_2008_string = "Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86)"
63
- end
64
-
65
- should 'return a string from #database_version that matches class regexp' do
66
- assert_match @version_regexp, @connection.database_version
67
- end
68
-
69
- should 'return a 4 digit year fixnum for #database_year' do
70
- assert_instance_of Fixnum, @connection.database_year
71
- assert_contains @supported_version, @connection.database_year
72
- end
73
-
74
- should 'return true to #sqlserver_2000?' do
75
- @connection.stubs(:database_version).returns(@sqlserver_2000_string)
76
- assert @connection.sqlserver_2000?
77
- end
78
-
79
- should 'return true to #sqlserver_2005?' do
80
- @connection.stubs(:database_version).returns(@sqlserver_2005_string)
81
- assert @connection.sqlserver_2005?
82
- end
83
-
84
- should 'return true to #sqlserver_2008?' do
85
- @connection.stubs(:database_version).returns(@sqlserver_2008_string)
86
- assert @connection.sqlserver_2008?
87
- end
88
-
89
- end
90
-
91
- context 'for #unqualify_table_name and #unqualify_db_name' do
92
-
93
- setup do
94
- @expected_table_name = 'baz'
95
- @expected_db_name = 'foo'
96
- @first_second_table_names = ['[baz]','baz','[bar].[baz]','bar.baz']
97
- @third_table_names = ['[foo].[bar].[baz]','foo.bar.baz']
98
- @qualifed_table_names = @first_second_table_names + @third_table_names
99
- end
100
-
101
- should 'return clean table_name from #unqualify_table_name' do
102
- @qualifed_table_names.each do |qtn|
103
- assert_equal @expected_table_name,
104
- @connection.send(:unqualify_table_name,qtn),
105
- "This qualifed_table_name #{qtn} did not unqualify correctly."
106
- end
107
- end
108
-
109
- should 'return nil from #unqualify_db_name when table_name is less than 2 qualified' do
110
- @first_second_table_names.each do |qtn|
111
- assert_equal nil, @connection.send(:unqualify_db_name,qtn),
112
- "This qualifed_table_name #{qtn} did not return nil."
113
- end
114
- end
115
-
116
- should 'return clean db_name from #unqualify_db_name when table is thrid level qualified' do
117
- @third_table_names.each do |qtn|
118
- assert_equal @expected_db_name,
119
- @connection.send(:unqualify_db_name,qtn),
120
- "This qualifed_table_name #{qtn} did not unqualify the db_name correctly."
121
- end
122
- end
123
-
124
- end
125
-
126
- should 'return true to #insert_sql? for inserts only' do
127
- assert @connection.send(:insert_sql?,'INSERT...')
128
- assert !@connection.send(:insert_sql?,'UPDATE...')
129
- assert !@connection.send(:insert_sql?,'SELECT...')
130
- end
131
-
132
- context 'for #limited_update_conditions' do
133
-
134
- should 'only match up to the first WHERE' do
135
- where_sql = "TOP 1 WHERE ([posts].author_id = 1 and [posts].columnWHEREname = 2) ORDER BY posts.id"
136
- assert_equal "WHERE bar IN (SELECT TOP 1 bar FROM foo WHERE ([posts].author_id = 1 and [posts].columnWHEREname = 2) ORDER BY posts.id)", @connection.limited_update_conditions(where_sql, 'foo', 'bar')
137
- end
138
-
139
- end
140
-
141
- context 'for #sql_for_association_limiting?' do
142
-
143
- should 'return false for simple selects with no GROUP BY and ORDER BY' do
144
- assert !sql_for_association_limiting?("SELECT * FROM [posts]")
145
- end
146
-
147
- should 'return true to single SELECT, ideally a table/primarykey, that also has a GROUP BY and ORDER BY' do
148
- assert sql_for_association_limiting?("SELECT [posts].id FROM...GROUP BY [posts].id ORDER BY MIN(posts.id)")
149
- end
150
-
151
- should 'return false to single * wildcard SELECT that also has a GROUP BY and ORDER BY' do
152
- assert !sql_for_association_limiting?("SELECT * FROM...GROUP BY [posts].id ORDER BY MIN(posts.id)")
153
- end
154
-
155
- should 'return false to multiple columns in the select even when GROUP BY and ORDER BY are present' do
156
- sql = "SELECT [accounts].credit_limit, firm_id FROM...GROUP BY firm_id ORDER BY firm_id"
157
- assert !sql_for_association_limiting?(sql)
158
- end
159
-
160
- end
161
-
162
- context 'for #get_table_name' do
163
-
164
- should 'return quoted table name from basic INSERT, UPDATE and SELECT statements' do
165
- assert_equal '[funny_jokes]', @connection.send(:get_table_name,@basic_insert_sql)
166
- assert_equal '[customers]', @connection.send(:get_table_name,@basic_update_sql)
167
- assert_equal '[customers]', @connection.send(:get_table_name,@basic_select_sql)
168
- end
169
-
170
- end
171
-
172
- context "for add_limit! within a scoped method call" do
173
- setup do
174
- @connection.stubs(:select_value).with(regexp_matches(/TotalRows/)).returns '100000000'
175
- end
176
-
177
- should 'not add any ordering if the scope doesn\'t have an order' do
178
- assert_equal 'SELECT * FROM (SELECT TOP 10 * FROM (SELECT TOP 40 * FROM [developers]) AS tmp1) AS tmp2', add_limit!('SELECT * FROM [developers]', {:offset => 30, :limit => 10}, {})
179
- end
180
-
181
- should 'still add the default ordering if the scope doesn\'t have an order but the raw order option is there' do
182
- assert_equal 'SELECT * FROM (SELECT TOP 10 * FROM (SELECT TOP 40 * FROM [developers]) AS tmp1 ORDER BY [name] DESC) AS tmp2 ORDER BY [name]', add_limit!('SELECT * FROM [developers]', {:offset => 30, :limit => 10, :order => 'name'}, {})
183
- end
184
-
185
- should 'add scoped order options to the offset and limit sql' do
186
- assert_equal 'SELECT * FROM (SELECT TOP 10 * FROM (SELECT TOP 40 * FROM [developers]) AS tmp1 ORDER BY [id] DESC) AS tmp2 ORDER BY [id]', add_limit!('SELECT * FROM [developers]', {:offset => 30, :limit => 10}, {:order => 'id'})
187
- end
188
-
189
- should 'combine scoped order with raw order options in the offset and limit sql' do
190
- assert_equal 'SELECT * FROM (SELECT TOP 10 * FROM (SELECT TOP 40 * FROM [developers]) AS tmp1 ORDER BY [name] DESC, [id] DESC) AS tmp2 ORDER BY [name], [id]', add_limit!('SELECT * FROM [developers]', {:offset => 30, :limit => 10, :order => 'name'}, {:order => 'id'})
191
- end
192
- end
193
-
194
- context 'dealing with various orders SQL snippets' do
195
-
196
- setup do
197
- @single_order = 'comments.id'
198
- @single_order_with_desc = 'comments.id DESC'
199
- @two_orders = 'comments.id, comments.post_id'
200
- @two_orders_with_asc = 'comments.id, comments.post_id ASC'
201
- @two_orders_with_desc_and_asc = 'comments.id DESC, comments.post_id ASC'
202
- @two_duplicate_order_with_dif_dir = "id, id DESC"
203
- end
204
-
205
- should 'convert to an 2D array of column/direction arrays using #orders_and_dirs_set' do
206
- assert_equal [['comments.id',nil]], orders_and_dirs_set('ORDER BY comments.id'), 'Needs to remove ORDER BY'
207
- assert_equal [['comments.id',nil]], orders_and_dirs_set(@single_order)
208
- assert_equal [['comments.id',nil],['comments.post_id',nil]], orders_and_dirs_set(@two_orders)
209
- assert_equal [['comments.id',nil],['comments.post_id','ASC']], orders_and_dirs_set(@two_orders_with_asc)
210
- assert_equal [['id',nil],['id','DESC']], orders_and_dirs_set(@two_duplicate_order_with_dif_dir)
211
- end
212
-
213
- should 'remove duplicate or maintain the same order by statements giving precedence to first using #add_order! method chain extension' do
214
- assert_equal ' ORDER BY comments.id', add_order!(@single_order)
215
- assert_equal ' ORDER BY comments.id DESC', add_order!(@single_order_with_desc)
216
- assert_equal ' ORDER BY comments.id, comments.post_id', add_order!(@two_orders)
217
- assert_equal ' ORDER BY comments.id DESC, comments.post_id ASC', add_order!(@two_orders_with_desc_and_asc)
218
- assert_equal 'SELECT * FROM [developers] ORDER BY id', add_order!('id, developers.id DESC','SELECT * FROM [developers]')
219
- assert_equal 'SELECT * FROM [developers] ORDER BY [developers].[id] DESC', add_order!('[developers].[id] DESC, id','SELECT * FROM [developers]')
220
- end
221
-
222
- should 'take all types of order options and convert them to MIN functions using #order_to_min_set' do
223
- assert_equal 'MIN(comments.id)', order_to_min_set(@single_order)
224
- assert_equal 'MIN(comments.id), MIN(comments.post_id)', order_to_min_set(@two_orders)
225
- assert_equal 'MIN(comments.id) DESC', order_to_min_set(@single_order_with_desc)
226
- assert_equal 'MIN(comments.id), MIN(comments.post_id) ASC', order_to_min_set(@two_orders_with_asc)
227
- assert_equal 'MIN(comments.id) DESC, MIN(comments.post_id) ASC', order_to_min_set(@two_orders_with_desc_and_asc)
228
- end
229
-
230
- should 'leave order by alone when same column crosses two tables' do
231
- assert_equal ' ORDER BY developers.name, projects.name', add_order!('developers.name, projects.name')
232
- end
233
-
234
- end
235
-
236
- context 'with different language' do
237
-
238
- teardown do
239
- @connection.execute("SET LANGUAGE us_english") rescue nil
240
- end
241
-
242
- should_eventually 'do a date insertion when language is german' do
243
- @connection.execute("SET LANGUAGE deutsch")
244
- assert_nothing_raised do
245
- Task.create(:starting => Time.utc(2000, 1, 31, 5, 42, 0), :ending => Date.new(2006, 12, 31))
246
- end
247
- end
248
-
249
- end
250
-
251
- context 'testing #enable_default_unicode_types configuration' do
252
-
253
- should 'use non-unicode types when set to false' do
254
- with_enable_default_unicode_types(false) do
255
- if sqlserver_2000?
256
- assert_equal 'varchar', @connection.native_string_database_type
257
- assert_equal 'text', @connection.native_text_database_type
258
- elsif sqlserver_2005?
259
- assert_equal 'varchar', @connection.native_string_database_type
260
- assert_equal 'varchar(max)', @connection.native_text_database_type
261
- end
262
- end
263
- end
264
-
265
- should 'use unicode types when set to true' do
266
- with_enable_default_unicode_types(true) do
267
- if sqlserver_2000?
268
- assert_equal 'nvarchar', @connection.native_string_database_type
269
- assert_equal 'ntext', @connection.native_text_database_type
270
- elsif sqlserver_2005?
271
- assert_equal 'nvarchar', @connection.native_string_database_type
272
- assert_equal 'nvarchar(max)', @connection.native_text_database_type
273
- end
274
- end
275
- end
276
-
277
- end
278
-
279
-
280
- end
281
-
282
- context 'For chronic data types' do
283
-
284
- context 'with a usec' do
285
-
286
- setup do
287
- @time = Time.now
288
- @db_datetime_003 = '2012-11-08 10:24:36.003'
289
- @db_datetime_123 = '2012-11-08 10:24:36.123'
290
- @all_datetimes = [@db_datetime_003, @db_datetime_123]
291
- @all_datetimes.each do |datetime|
292
- @connection.execute("INSERT INTO [sql_server_chronics] ([datetime]) VALUES('#{datetime}')")
293
- end
294
- end
295
-
296
- teardown do
297
- @all_datetimes.each do |datetime|
298
- @connection.execute("DELETE FROM [sql_server_chronics] WHERE [datetime] = '#{datetime}'")
299
- end
300
- end
301
-
302
- context 'finding existing DB objects' do
303
-
304
- should 'find 003 millisecond in the DB with before and after casting' do
305
- existing_003 = SqlServerChronic.find_by_datetime!(@db_datetime_003)
306
- assert_equal @db_datetime_003, existing_003.datetime_before_type_cast
307
- assert_equal 3000, existing_003.datetime.usec, 'A 003 millisecond in SQL Server is 3000 microseconds'
308
- end
309
-
310
- should 'find 123 millisecond in the DB with before and after casting' do
311
- existing_123 = SqlServerChronic.find_by_datetime!(@db_datetime_123)
312
- assert_equal @db_datetime_123, existing_123.datetime_before_type_cast
313
- assert_equal 123000, existing_123.datetime.usec, 'A 123 millisecond in SQL Server is 123000 microseconds'
314
- end
315
-
316
- end
317
-
318
- context 'saving new datetime objects' do
319
-
320
- should 'truncate 123456 usec to just 123 in the DB cast back to 123000' do
321
- @time.stubs(:usec).returns(123456)
322
- saved = SqlServerChronic.create!(:datetime => @time).reload
323
- assert_equal '123', saved.datetime_before_type_cast.split('.')[1]
324
- assert_equal 123000, saved.datetime.usec
325
- end
326
-
327
- should 'truncate 3001 usec to just 003 in the DB cast back to 3000' do
328
- @time.stubs(:usec).returns(3001)
329
- saved = SqlServerChronic.create!(:datetime => @time).reload
330
- assert_equal '003', saved.datetime_before_type_cast.split('.')[1]
331
- assert_equal 3000, saved.datetime.usec
332
- end
333
-
334
- end
335
-
336
- end
337
-
338
- end
339
-
340
- context 'For identity inserts' do
341
-
342
- setup do
343
- @identity_insert_sql = "INSERT INTO [funny_jokes] ([id],[name]) VALUES(420,'Knock knock')"
344
- @identity_insert_sql_unquoted = "INSERT INTO funny_jokes (id, name) VALUES(420, 'Knock knock')"
345
- @identity_insert_sql_unordered = "INSERT INTO [funny_jokes] ([name],[id]) VALUES('Knock knock',420)"
346
- end
347
-
348
- should 'return quoted table_name to #query_requires_identity_insert? when INSERT sql contains id column' do
349
- assert_equal '[funny_jokes]', @connection.send(:query_requires_identity_insert?,@identity_insert_sql)
350
- assert_equal '[funny_jokes]', @connection.send(:query_requires_identity_insert?,@identity_insert_sql_unquoted)
351
- assert_equal '[funny_jokes]', @connection.send(:query_requires_identity_insert?,@identity_insert_sql_unordered)
352
- end
353
-
354
- should 'return false to #query_requires_identity_insert? for normal SQL' do
355
- [@basic_insert_sql, @basic_update_sql, @basic_select_sql].each do |sql|
356
- assert !@connection.send(:query_requires_identity_insert?,sql), "SQL was #{sql}"
357
- end
358
- end
359
-
360
- should 'find identity column using #identity_column' do
361
- joke_id_column = Joke.columns.detect { |c| c.name == 'id' }
362
- assert_equal joke_id_column, @connection.send(:identity_column,Joke.table_name)
363
- end
364
-
365
- should 'return nil when calling #identity_column for a table_name with no identity' do
366
- assert_nil @connection.send(:identity_column,Subscriber.table_name)
367
- end
368
-
369
- end
370
-
371
- context 'For Quoting' do
372
-
373
- should 'return 1 for #quoted_true' do
374
- assert_equal '1', @connection.quoted_true
375
- end
376
-
377
- should 'return 0 for #quoted_false' do
378
- assert_equal '0', @connection.quoted_false
379
- end
380
-
381
- should 'not escape backslash characters like abstract adapter' do
382
- string_with_backslashs = "\\n"
383
- assert_equal string_with_backslashs, @connection.quote_string(string_with_backslashs)
384
- end
385
-
386
- should 'quote column names with brackets' do
387
- assert_equal '[foo]', @connection.quote_column_name(:foo)
388
- assert_equal '[foo]', @connection.quote_column_name('foo')
389
- assert_equal '[foo].[bar]', @connection.quote_column_name('foo.bar')
390
- end
391
-
392
- should 'not quote already quoted column names with brackets' do
393
- assert_equal '[foo]', @connection.quote_column_name('[foo]')
394
- assert_equal '[foo].[bar]', @connection.quote_column_name('[foo].[bar]')
395
- end
396
-
397
- should 'quote table names like columns' do
398
- assert_equal '[foo].[bar]', @connection.quote_column_name('foo.bar')
399
- assert_equal '[foo].[bar].[baz]', @connection.quote_column_name('foo.bar.baz')
400
- end
401
-
402
- end
403
-
404
- context 'When disableing referential integrity' do
405
-
406
- setup do
407
- @parent = FkTestHasPk.create!
408
- @member = FkTestHasFk.create!(:fk_id => @parent.id)
409
- end
410
-
411
- should 'NOT ALLOW by default the deletion of a referenced parent' do
412
- FkTestHasPk.connection.disable_referential_integrity { }
413
- assert_raise(ActiveRecord::StatementInvalid) { @parent.destroy }
414
- end
415
-
416
- should 'ALLOW deletion of referenced parent using #disable_referential_integrity block' do
417
- FkTestHasPk.connection.disable_referential_integrity { @parent.destroy }
418
- end
419
-
420
- should 'again NOT ALLOW deletion of referenced parent after #disable_referential_integrity block' do
421
- assert_raise(ActiveRecord::StatementInvalid) do
422
- FkTestHasPk.connection.disable_referential_integrity { }
423
- @parent.destroy
424
- end
425
- end
426
-
427
- end
428
-
429
- context 'For DatabaseStatements' do
430
-
431
- context "finding out what user_options are available" do
432
-
433
- should "run the database consistency checker useroptions command" do
434
- @connection.expects(:select_rows).with(regexp_matches(/^dbcc\s+useroptions$/i)).returns []
435
- @connection.user_options
436
- end
437
-
438
- should "return a underscored key hash with indifferent access of the results" do
439
- @connection.expects(:select_rows).with(regexp_matches(/^dbcc\s+useroptions$/i)).returns [['some', 'thing'], ['isolation level', 'read uncommitted']]
440
- uo = @connection.user_options
441
- assert_equal 2, uo.keys.size
442
- assert_equal 'thing', uo['some']
443
- assert_equal 'thing', uo[:some]
444
- assert_equal 'read uncommitted', uo['isolation_level']
445
- assert_equal 'read uncommitted', uo[:isolation_level]
446
- end
447
-
448
- end
449
-
450
- context "altering isolation levels" do
451
-
452
- should "barf if the requested isolation level is not valid" do
453
- assert_raise(ArgumentError) do
454
- @connection.run_with_isolation_level 'INVALID ISOLATION LEVEL' do; end
455
- end
456
- end
457
-
458
- context "with a valid isolation level" do
459
-
460
- setup do
461
- @t1 = tasks(:first_task)
462
- @t2 = tasks(:another_task)
463
- assert @t1, 'Tasks :first_task should be in AR fixtures'
464
- assert @t2, 'Tasks :another_task should be in AR fixtures'
465
- good_isolation_level = @connection.user_options[:isolation_level].blank? || @connection.user_options[:isolation_level] =~ /read committed/i
466
- assert good_isolation_level, "User isolation level is not at a happy starting place: #{@connection.user_options[:isolation_level].inspect}"
467
- end
468
-
469
- should 'allow #run_with_isolation_level to not take a block to set it' do
470
- begin
471
- @connection.run_with_isolation_level 'READ UNCOMMITTED'
472
- assert_match %r|read uncommitted|i, @connection.user_options[:isolation_level]
473
- ensure
474
- @connection.run_with_isolation_level 'READ COMMITTED'
475
- end
476
- end
477
-
478
- should 'return block value using #run_with_isolation_level' do
479
- assert_same_elements Task.find(:all), @connection.run_with_isolation_level('READ UNCOMMITTED') { Task.find(:all) }
480
- end
481
-
482
- should 'pass a read uncommitted isolation level test' do
483
- assert_nil @t2.starting, 'Fixture should have this empty.'
484
- begin
485
- Task.transaction do
486
- @t2.starting = Time.now
487
- @t2.save
488
- @dirty_t2 = @connection.run_with_isolation_level('READ UNCOMMITTED') { Task.find(@t2.id) }
489
- raise ActiveRecord::ActiveRecordError
490
- end
491
- rescue
492
- 'Do Nothing'
493
- end
494
- assert @dirty_t2, 'Should have a Task record from within block above.'
495
- assert @dirty_t2.starting, 'Should have a dirty date.'
496
- assert_nil Task.find(@t2.id).starting, 'Should be nil again from botched transaction above.'
497
- end unless active_record_2_point_2? # Transactions in tests are a bit screwy in 2.2.
498
-
499
- end
500
-
501
- end
502
-
503
- end
504
-
505
- context 'For SchemaStatements' do
506
-
507
- context 'returning from #type_to_sql' do
508
-
509
- should 'create integers when no limit supplied' do
510
- assert_equal 'integer', @connection.type_to_sql(:integer)
511
- end
512
-
513
- should 'create integers when limit is 4' do
514
- assert_equal 'integer', @connection.type_to_sql(:integer, 4)
515
- end
516
-
517
- should 'create integers when limit is 3' do
518
- assert_equal 'integer', @connection.type_to_sql(:integer, 3)
519
- end
520
-
521
- should 'create smallints when limit is less than 3' do
522
- assert_equal 'smallint', @connection.type_to_sql(:integer, 2)
523
- assert_equal 'smallint', @connection.type_to_sql(:integer, 1)
524
- end
525
-
526
- should 'create bigints when limit is greateer than 4' do
527
- assert_equal 'bigint', @connection.type_to_sql(:integer, 5)
528
- assert_equal 'bigint', @connection.type_to_sql(:integer, 6)
529
- assert_equal 'bigint', @connection.type_to_sql(:integer, 7)
530
- assert_equal 'bigint', @connection.type_to_sql(:integer, 8)
531
- end
532
-
533
- end
534
-
535
- end
536
-
537
- context 'For indexes' do
538
-
539
- setup do
540
- @desc_index_name = 'idx_credit_limit_test_desc'
541
- @connection.execute "CREATE INDEX #{@desc_index_name} ON accounts (credit_limit DESC)"
542
- end
543
-
544
- teardown do
545
- @connection.execute "DROP INDEX accounts.#{@desc_index_name}"
546
- end
547
-
548
- should 'have indexes with descending order' do
549
- assert @connection.indexes('accounts').detect { |i| i.name == @desc_index_name }
550
- end
551
-
552
- end
553
-
554
- context 'For views' do
555
-
556
- context 'using @connection.views' do
557
-
558
- should 'return an array' do
559
- assert_instance_of Array, @connection.views
560
- end
561
-
562
- should 'find CustomersView table name' do
563
- assert_contains @connection.views, 'customers_view'
564
- end
565
-
566
- should 'not contain system views' do
567
- systables = ['sysconstraints','syssegments']
568
- systables.each do |systable|
569
- assert !@connection.views.include?(systable), "This systable #{systable} should not be in the views array."
570
- end
571
- end
572
-
573
- should 'allow the connection.view_information method to return meta data on the view' do
574
- view_info = @connection.view_information('customers_view')
575
- assert_equal('customers_view', view_info['TABLE_NAME'])
576
- assert_match(/CREATE VIEW customers_view/, view_info['VIEW_DEFINITION'])
577
- end
578
-
579
- should 'allow the connection.view_table_name method to return true table_name for the view' do
580
- assert_equal 'customers', @connection.view_table_name('customers_view')
581
- assert_equal 'topics', @connection.view_table_name('topics'), 'No view here, the same table name should come back.'
582
- end
583
-
584
- end
585
-
586
- context 'used by a class for table_name' do
587
-
588
- context 'with same column names' do
589
-
590
- should 'have matching column objects' do
591
- columns = ['id','name','balance']
592
- assert !CustomersView.columns.blank?
593
- assert_equal columns.size, CustomersView.columns.size
594
- columns.each do |colname|
595
- assert_instance_of ActiveRecord::ConnectionAdapters::SQLServerColumn,
596
- CustomersView.columns_hash[colname],
597
- "Column name #{colname.inspect} was not found in these columns #{CustomersView.columns.map(&:name).inspect}"
598
- end
599
- end
600
-
601
- should 'find identity column' do
602
- assert CustomersView.columns_hash['id'].primary
603
- assert CustomersView.columns_hash['id'].is_identity?
604
- end
605
-
606
- should 'find default values' do
607
- assert_equal 0, CustomersView.new.balance
608
- end
609
-
610
- should 'respond true to table_exists?' do
611
- assert CustomersView.table_exists?
612
- end
613
-
614
- should 'have correct table name for all column objects' do
615
- assert CustomersView.columns.all?{ |c| c.table_name == 'customers_view' },
616
- CustomersView.columns.map(&:table_name).inspect
617
- end
618
-
619
- end
620
-
621
- context 'with aliased column names' do
622
-
623
- should 'have matching column objects' do
624
- columns = ['id','pretend_null']
625
- assert !StringDefaultsView.columns.blank?
626
- assert_equal columns.size, StringDefaultsView.columns.size
627
- columns.each do |colname|
628
- assert_instance_of ActiveRecord::ConnectionAdapters::SQLServerColumn,
629
- StringDefaultsView.columns_hash[colname],
630
- "Column name #{colname.inspect} was not found in these columns #{StringDefaultsView.columns.map(&:name).inspect}"
631
- end
632
- end
633
-
634
- should 'find identity column' do
635
- assert StringDefaultsView.columns_hash['id'].primary
636
- assert StringDefaultsView.columns_hash['id'].is_identity?
637
- end
638
-
639
- should 'find default values' do
640
- assert_equal 'null', StringDefaultsView.new.pretend_null,
641
- StringDefaultsView.columns_hash['pretend_null'].inspect
642
- end
643
-
644
- should 'respond true to table_exists?' do
645
- assert StringDefaultsView.table_exists?
646
- end
647
-
648
- should 'have correct table name for all column objects' do
649
- assert StringDefaultsView.columns.all?{ |c| c.table_name == 'string_defaults_view' },
650
- StringDefaultsView.columns.map(&:table_name).inspect
651
- end
652
-
653
- end
654
-
655
- end
656
-
657
- context 'doing identity inserts' do
658
-
659
- setup do
660
- @view_insert_sql = "INSERT INTO [customers_view] ([id],[name],[balance]) VALUES (420,'Microsoft',0)"
661
- end
662
-
663
- should 'respond true/tablename to #query_requires_identity_insert?' do
664
- assert_equal '[customers_view]', @connection.send(:query_requires_identity_insert?,@view_insert_sql)
665
- end
666
-
667
- should 'be able to do an identity insert' do
668
- assert_nothing_raised { @connection.execute(@view_insert_sql) }
669
- assert CustomersView.find(420)
670
- end
671
-
672
- end
673
-
674
- context 'that have more than 4000 chars for their defintion' do
675
-
676
- should 'cope with null returned for the defintion' do
677
- assert_nothing_raised() { StringDefaultsBigView.columns }
678
- end
679
-
680
- should 'using alternate view defintion still be able to find real default' do
681
- assert_equal 'null', StringDefaultsBigView.new.pretend_null,
682
- StringDefaultsBigView.columns_hash['pretend_null'].inspect
683
- end
684
-
685
- end
686
-
687
- end
688
-
689
-
690
-
691
- private
692
-
693
- def sql_for_association_limiting?(sql)
694
- @connection.send :sql_for_association_limiting?, sql
695
- end
696
-
697
- def orders_and_dirs_set(order)
698
- @connection.send :orders_and_dirs_set, order
699
- end
700
-
701
- def add_order!(order,sql='')
702
- ActiveRecord::Base.send :add_order!, sql, order, nil
703
- sql
704
- end
705
-
706
- def add_limit!(sql, options, scope = :auto)
707
- ActiveRecord::Base.send :add_limit!, sql, options, scope
708
- sql
709
- end
710
-
711
- def order_to_min_set(order)
712
- @connection.send :order_to_min_set, order
713
- end
714
-
715
- def with_enable_default_unicode_types(setting)
716
- old_setting = ActiveRecord::ConnectionAdapters::SQLServerAdapter.enable_default_unicode_types
717
- old_text = ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_text_database_type
718
- old_string = ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_string_database_type
719
- ActiveRecord::ConnectionAdapters::SQLServerAdapter.enable_default_unicode_types = setting
720
- ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_text_database_type = nil
721
- ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_string_database_type = nil
722
- yield
723
- ensure
724
- ActiveRecord::ConnectionAdapters::SQLServerAdapter.enable_default_unicode_types = old_setting
725
- ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_text_database_type = old_text
726
- ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_string_database_type = old_string
727
- end
728
-
729
- end
730
-
731
-
732
- class AdapterTest < ActiveRecord::TestCase
733
-
734
- COERCED_TESTS = [
735
- :test_add_limit_offset_should_sanitize_sql_injection_for_limit_without_comas,
736
- :test_add_limit_offset_should_sanitize_sql_injection_for_limit_with_comas
737
- ]
738
-
739
- include SqlserverCoercedTest
740
-
741
- def test_coerced_test_add_limit_offset_should_sanitize_sql_injection_for_limit_without_comas
742
- sql_inject = "1 select * from schema"
743
- connection = ActiveRecord::Base.connection
744
- assert_raise(ArgumentError) { connection.add_limit_offset!("", :limit=>sql_inject) }
745
- assert_raise(ArgumentError) { connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7) }
746
- end
747
-
748
- def test_coerced_test_add_limit_offset_should_sanitize_sql_injection_for_limit_with_comas
749
- sql_inject = "1, 7 procedure help()"
750
- connection = ActiveRecord::Base.connection
751
- assert_raise(ArgumentError) { connection.add_limit_offset!("", :limit=>sql_inject) }
752
- assert_raise(ArgumentError) { connection.add_limit_offset!("", :limit=> '1 ; DROP TABLE USERS', :offset=>7) }
753
- assert_raise(ArgumentError) { connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7) }
754
- end
755
-
756
- end