activerecord-sqlserver-adapter 2.3.7 → 3.2.18
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG +385 -61
- data/MIT-LICENSE +1 -1
- data/VERSION +1 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +42 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/database_statements.rb +97 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +41 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +26 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb +38 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/relation.rb +19 -0
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +49 -0
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +458 -0
- data/lib/active_record/connection_adapters/sqlserver/errors.rb +36 -0
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +113 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +85 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +376 -0
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +69 -0
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +25 -0
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +67 -0
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +32 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +344 -1055
- data/lib/arel/visitors/sqlserver.rb +389 -0
- metadata +60 -83
- data/README.rdoc +0 -190
- data/RUNNING_UNIT_TESTS +0 -65
- data/Rakefile +0 -41
- data/autotest/discover.rb +0 -4
- data/autotest/railssqlserver.rb +0 -16
- data/autotest/sqlserver.rb +0 -54
- data/lib/active_record/connection_adapters/sqlserver_adapter/core_ext/active_record.rb +0 -151
- data/lib/active_record/connection_adapters/sqlserver_adapter/core_ext/odbc.rb +0 -40
- data/test/cases/aaaa_create_tables_test_sqlserver.rb +0 -19
- data/test/cases/adapter_test_sqlserver.rb +0 -756
- data/test/cases/attribute_methods_test_sqlserver.rb +0 -33
- data/test/cases/basics_test_sqlserver.rb +0 -21
- data/test/cases/calculations_test_sqlserver.rb +0 -20
- data/test/cases/column_test_sqlserver.rb +0 -285
- data/test/cases/connection_test_sqlserver.rb +0 -146
- data/test/cases/eager_association_test_sqlserver.rb +0 -42
- data/test/cases/execute_procedure_test_sqlserver.rb +0 -44
- data/test/cases/inheritance_test_sqlserver.rb +0 -28
- data/test/cases/method_scoping_test_sqlserver.rb +0 -28
- data/test/cases/migration_test_sqlserver.rb +0 -123
- data/test/cases/named_scope_test_sqlserver.rb +0 -21
- data/test/cases/offset_and_limit_test_sqlserver.rb +0 -108
- data/test/cases/pessimistic_locking_test_sqlserver.rb +0 -125
- data/test/cases/query_cache_test_sqlserver.rb +0 -24
- data/test/cases/schema_dumper_test_sqlserver.rb +0 -72
- data/test/cases/specific_schema_test_sqlserver.rb +0 -97
- data/test/cases/sqlserver_helper.rb +0 -127
- data/test/cases/table_name_test_sqlserver.rb +0 -38
- data/test/cases/transaction_test_sqlserver.rb +0 -93
- data/test/cases/unicode_test_sqlserver.rb +0 -50
- data/test/cases/validations_test_sqlserver.rb +0 -35
- data/test/connections/native_sqlserver/connection.rb +0 -25
- data/test/connections/native_sqlserver_odbc/connection.rb +0 -27
- data/test/migrations/transaction_table/1_table_will_never_be_created.rb +0 -11
- 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
|