tiny_tds_vagas 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/CHANGELOG +198 -0
  4. data/CODE_OF_CONDUCT.md +31 -0
  5. data/Gemfile +6 -0
  6. data/ISSUE_TEMPLATE.md +5 -0
  7. data/MIT-LICENSE +23 -0
  8. data/README.md +427 -0
  9. data/Rakefile +110 -0
  10. data/VERSION +1 -0
  11. data/appveyor.yml +52 -0
  12. data/bin/defncopy +3 -0
  13. data/bin/tsql +3 -0
  14. data/ext/tiny_tds/client.c +410 -0
  15. data/ext/tiny_tds/client.h +49 -0
  16. data/ext/tiny_tds/extconf.rb +329 -0
  17. data/ext/tiny_tds/extconsts.rb +15 -0
  18. data/ext/tiny_tds/result.c +608 -0
  19. data/ext/tiny_tds/result.h +32 -0
  20. data/ext/tiny_tds/tiny_tds_ext.c +12 -0
  21. data/ext/tiny_tds/tiny_tds_ext.h +17 -0
  22. data/lib/tiny_tds.rb +37 -0
  23. data/lib/tiny_tds/bin.rb +86 -0
  24. data/lib/tiny_tds/client.rb +124 -0
  25. data/lib/tiny_tds/error.rb +15 -0
  26. data/lib/tiny_tds/result.rb +8 -0
  27. data/lib/tiny_tds/version.rb +3 -0
  28. data/ports/patches/freetds/1.00/0001-mingw_missing_inet_pton.diff +34 -0
  29. data/test/appveyor/dbsetup.ps1 +27 -0
  30. data/test/appveyor/dbsetup.sql +9 -0
  31. data/test/benchmark/query.rb +77 -0
  32. data/test/benchmark/query_odbc.rb +106 -0
  33. data/test/benchmark/query_tinytds.rb +126 -0
  34. data/test/client_test.rb +217 -0
  35. data/test/result_test.rb +728 -0
  36. data/test/schema/1px.gif +0 -0
  37. data/test/schema/sqlserver_2000.sql +140 -0
  38. data/test/schema/sqlserver_2005.sql +140 -0
  39. data/test/schema/sqlserver_2008.sql +140 -0
  40. data/test/schema/sqlserver_2014.sql +140 -0
  41. data/test/schema/sqlserver_azure.sql +140 -0
  42. data/test/schema/sybase_ase.sql +138 -0
  43. data/test/schema_test.rb +443 -0
  44. data/test/test_helper.rb +213 -0
  45. data/test/thread_test.rb +98 -0
  46. data/tiny_tds.gemspec +28 -0
  47. metadata +201 -0
@@ -0,0 +1,728 @@
1
+ # encoding: utf-8
2
+ require 'test_helper'
3
+
4
+ class ResultTest < TinyTds::TestCase
5
+
6
+ describe 'Basic query and result' do
7
+
8
+ before do
9
+ @@current_schema_loaded ||= load_current_schema
10
+ @client = new_connection
11
+ @query1 = 'SELECT 1 AS [one]'
12
+ end
13
+
14
+ it 'has included Enumerable' do
15
+ assert TinyTds::Result.ancestors.include?(Enumerable)
16
+ end
17
+
18
+ it 'responds to #each' do
19
+ result = @client.execute(@query1)
20
+ assert result.respond_to?(:each)
21
+ end
22
+
23
+ it 'returns all results for #each with no block' do
24
+ result = @client.execute(@query1)
25
+ data = result.each
26
+ row = data.first
27
+ assert_instance_of Array, data
28
+ assert_equal 1, data.size
29
+ assert_instance_of Hash, row, 'hash is the default query option'
30
+ end
31
+
32
+ it 'returns all results for #each with a block yielding a row at a time' do
33
+ result = @client.execute(@query1)
34
+ data = result.each do |row|
35
+ assert_instance_of Hash, row, 'hash is the default query option'
36
+ end
37
+ assert_instance_of Array, data
38
+ end
39
+
40
+ it 'allows successive calls to each returning the same data' do
41
+ result = @client.execute(@query1)
42
+ data = result.each
43
+ result.each
44
+ assert_equal data.object_id, result.each.object_id
45
+ assert_equal data.first.object_id, result.each.first.object_id
46
+ end
47
+
48
+ it 'returns hashes with string keys' do
49
+ result = @client.execute(@query1)
50
+ row = result.each(:as => :hash, :symbolize_keys => false).first
51
+ assert_instance_of Hash, row
52
+ assert_equal ['one'], row.keys
53
+ assert_equal ['one'], result.fields
54
+ end
55
+
56
+ it 'returns hashes with symbol keys' do
57
+ result = @client.execute(@query1)
58
+ row = result.each(:as => :hash, :symbolize_keys => true).first
59
+ assert_instance_of Hash, row
60
+ assert_equal [:one], row.keys
61
+ assert_equal [:one], result.fields
62
+ end
63
+
64
+ it 'returns arrays with string fields' do
65
+ result = @client.execute(@query1)
66
+ row = result.each(:as => :array, :symbolize_keys => false).first
67
+ assert_instance_of Array, row
68
+ assert_equal ['one'], result.fields
69
+ end
70
+
71
+ it 'returns arrays with symbol fields' do
72
+ result = @client.execute(@query1)
73
+ row = result.each(:as => :array, :symbolize_keys => true).first
74
+ assert_instance_of Array, row
75
+ assert_equal [:one], result.fields
76
+ end
77
+
78
+ it 'allows sql concat + to work' do
79
+ rollback_transaction(@client) do
80
+ @client.execute("DELETE FROM [datatypes]").do
81
+ @client.execute("INSERT INTO [datatypes] ([char_10], [varchar_50]) VALUES ('1', '2')").do
82
+ result = @client.execute("SELECT TOP (1) [char_10] + 'test' + [varchar_50] AS [test] FROM [datatypes]").each.first['test']
83
+ result.must_equal "1 test2"
84
+ end
85
+ end
86
+
87
+ it 'must be able to turn :cache_rows option off' do
88
+ result = @client.execute(@query1)
89
+ local = []
90
+ result.each(:cache_rows => false) do |row|
91
+ local << row
92
+ end
93
+ assert local.first, 'should have iterated over each row'
94
+ assert_equal [], result.each, 'should not have been cached'
95
+ assert_equal ['one'], result.fields, 'should still cache field names'
96
+ end
97
+
98
+ it 'must be able to get the first result row only' do
99
+ load_current_schema
100
+ big_query = "SELECT [id] FROM [datatypes]"
101
+ one = @client.execute(big_query).each(:first => true)
102
+ many = @client.execute(big_query).each
103
+ assert many.size > 1
104
+ assert one.size == 1
105
+ end
106
+
107
+ it 'copes with no results when using first option' do
108
+ data = @client.execute("SELECT [id] FROM [datatypes] WHERE [id] = -1").each(:first => true)
109
+ assert_equal [], data
110
+ end
111
+
112
+ it 'must delete, insert and find data' do
113
+ rollback_transaction(@client) do
114
+ text = 'test insert and delete'
115
+ @client.execute("DELETE FROM [datatypes] WHERE [varchar_50] IS NOT NULL").do
116
+ @client.execute("INSERT INTO [datatypes] ([varchar_50]) VALUES ('#{text}')").do
117
+ row = @client.execute("SELECT [varchar_50] FROM [datatypes] WHERE [varchar_50] IS NOT NULL").each.first
118
+ assert row
119
+ assert_equal text, row['varchar_50']
120
+ end
121
+ end
122
+
123
+ it 'must insert and find unicode data' do
124
+ rollback_transaction(@client) do
125
+ text = '😍'
126
+ @client.execute("DELETE FROM [datatypes] WHERE [nvarchar_50] IS NOT NULL").do
127
+ @client.execute("INSERT INTO [datatypes] ([nvarchar_50]) VALUES (N'#{text}')").do
128
+ row = @client.execute("SELECT [nvarchar_50] FROM [datatypes] WHERE [nvarchar_50] IS NOT NULL").each.first
129
+ assert_equal text, row['nvarchar_50']
130
+ end
131
+ end
132
+
133
+ it 'must delete and update with affected rows support and insert with identity support in native sql' do
134
+ rollback_transaction(@client) do
135
+ text = 'test affected rows sql'
136
+ @client.execute("DELETE FROM [datatypes]").do
137
+ afrows = @client.execute("SELECT @@ROWCOUNT AS AffectedRows").each.first['AffectedRows']
138
+ assert_instance_of Fixnum, afrows
139
+ @client.execute("INSERT INTO [datatypes] ([varchar_50]) VALUES ('#{text}')").do
140
+ pk1 = @client.execute(@client.identity_sql).each.first['Ident']
141
+ assert_instance_of Fixnum, pk1, 'we it be able to CAST to bigint'
142
+ @client.execute("UPDATE [datatypes] SET [varchar_50] = NULL WHERE [varchar_50] = '#{text}'").do
143
+ afrows = @client.execute("SELECT @@ROWCOUNT AS AffectedRows").each.first['AffectedRows']
144
+ assert_equal 1, afrows
145
+ end
146
+ end
147
+
148
+ it 'has a #do method that cancels result rows and returns affected rows natively' do
149
+ rollback_transaction(@client) do
150
+ text = 'test affected rows native'
151
+ count = @client.execute("SELECT COUNT(*) AS [count] FROM [datatypes]").each.first['count']
152
+ deleted_rows = @client.execute("DELETE FROM [datatypes]").do
153
+ assert_equal count, deleted_rows, 'should have deleted rows equal to count'
154
+ inserted_rows = @client.execute("INSERT INTO [datatypes] ([varchar_50]) VALUES ('#{text}')").do
155
+ assert_equal 1, inserted_rows, 'should have inserted row for one above'
156
+ updated_rows = @client.execute("UPDATE [datatypes] SET [varchar_50] = NULL WHERE [varchar_50] = '#{text}'").do
157
+ assert_equal 1, updated_rows, 'should have updated row for one above' unless sqlserver_2000? # Will report -1
158
+ end
159
+ end
160
+
161
+ it 'allows native affected rows using #do to work under transaction' do
162
+ rollback_transaction(@client) do
163
+ text = 'test affected rows native in transaction'
164
+ @client.execute("BEGIN TRANSACTION").do
165
+ @client.execute("DELETE FROM [datatypes]").do
166
+ inserted_rows = @client.execute("INSERT INTO [datatypes] ([varchar_50]) VALUES ('#{text}')").do
167
+ assert_equal 1, inserted_rows, 'should have inserted row for one above'
168
+ updated_rows = @client.execute("UPDATE [datatypes] SET [varchar_50] = NULL WHERE [varchar_50] = '#{text}'").do
169
+ assert_equal 1, updated_rows, 'should have updated row for one above' unless sqlserver_2000? # Will report -1
170
+ end
171
+ end
172
+
173
+ it 'has an #insert method that cancels result rows and returns IDENTITY natively' do
174
+ rollback_transaction(@client) do
175
+ text = 'test scope identity rows native'
176
+ @client.execute("DELETE FROM [datatypes] WHERE [varchar_50] = '#{text}'").do
177
+ @client.execute("INSERT INTO [datatypes] ([varchar_50]) VALUES ('#{text}')").do
178
+ sql_identity = @client.execute(@client.identity_sql).each.first['Ident']
179
+ native_identity = @client.execute("INSERT INTO [datatypes] ([varchar_50]) VALUES ('#{text}')").insert
180
+ assert_equal sql_identity+1, native_identity
181
+ end
182
+ end
183
+
184
+ it 'returns bigint for #insert when needed' do
185
+ return if sqlserver_azure? # We can not alter clustered index like this test does.
186
+ return if sybase_ase? # On Sybase, sp_helpindex cannot be used inside a transaction since it does a
187
+ # 'CREATE TABLE' command is not allowed within a multi-statement transaction
188
+ # and and sp_helpindex creates a temporary table #spindtab.
189
+ rollback_transaction(@client) do
190
+ seed = 9223372036854775805
191
+ @client.execute("DELETE FROM [datatypes]").do
192
+ id_constraint_name = @client.execute("EXEC sp_helpindex [datatypes]").detect{ |row| row['index_keys'] == 'id' }['index_name']
193
+ @client.execute("ALTER TABLE [datatypes] DROP CONSTRAINT [#{id_constraint_name}]").do
194
+ @client.execute("ALTER TABLE [datatypes] DROP COLUMN [id]").do
195
+ @client.execute("ALTER TABLE [datatypes] ADD [id] [bigint] NOT NULL IDENTITY(1,1) PRIMARY KEY").do
196
+ @client.execute("DBCC CHECKIDENT ('datatypes', RESEED, #{seed})").do
197
+ identity = @client.execute("INSERT INTO [datatypes] ([varchar_50]) VALUES ('something')").insert
198
+ assert_equal seed, identity
199
+ end
200
+ end
201
+
202
+ it 'must be able to begin/commit transactions with raw sql' do
203
+ rollback_transaction(@client) do
204
+ @client.execute("BEGIN TRANSACTION").do
205
+ @client.execute("DELETE FROM [datatypes]").do
206
+ @client.execute("COMMIT TRANSACTION").do
207
+ count = @client.execute("SELECT COUNT(*) AS [count] FROM [datatypes]").each.first['count']
208
+ assert_equal 0, count
209
+ end
210
+ end
211
+
212
+ it 'must be able to begin/rollback transactions with raw sql' do
213
+ load_current_schema
214
+ @client.execute("BEGIN TRANSACTION").do
215
+ @client.execute("DELETE FROM [datatypes]").do
216
+ @client.execute("ROLLBACK TRANSACTION").do
217
+ count = @client.execute("SELECT COUNT(*) AS [count] FROM [datatypes]").each.first['count']
218
+ 0.wont_equal count
219
+ end
220
+
221
+ it 'has a #fields accessor with logic default and valid outcome' do
222
+ result = @client.execute(@query1)
223
+ result.fields.must_equal ['one']
224
+ result.each
225
+ result.fields.must_equal ['one']
226
+ end
227
+
228
+ it 'always returns an array for fields for all sql' do
229
+ result = @client.execute("USE [tinytdstest]")
230
+ result.fields.must_equal []
231
+ result.do
232
+ result.fields.must_equal []
233
+ end
234
+
235
+ it 'returns fields even when no results are found' do
236
+ no_results_query = "SELECT [id], [varchar_50] FROM [datatypes] WHERE [varchar_50] = 'NOTFOUND'"
237
+ # Fields before each.
238
+ result = @client.execute(no_results_query)
239
+ result.fields.must_equal ['id','varchar_50']
240
+ result.each
241
+ result.fields.must_equal ['id','varchar_50']
242
+ # Each then fields
243
+ result = @client.execute(no_results_query)
244
+ result.each
245
+ result.fields.must_equal ['id','varchar_50']
246
+ end
247
+
248
+ it 'allows the result to be canceled before reading' do
249
+ result = @client.execute(@query1)
250
+ result.cancel
251
+ @client.execute(@query1).each
252
+ end
253
+
254
+ it 'works in tandem with the client when needing to find out if client has sql sent and result is canceled or not' do
255
+ # Default state.
256
+ @client = TinyTds::Client.new(connection_options)
257
+ @client.sqlsent?.must_equal false
258
+ @client.canceled?.must_equal false
259
+ # With active result before and after cancel.
260
+ result = @client.execute(@query1)
261
+ @client.sqlsent?.must_equal true
262
+ @client.canceled?.must_equal false
263
+ result.cancel
264
+ @client.sqlsent?.must_equal false
265
+ @client.canceled?.must_equal true
266
+ assert result.cancel, 'must be safe to call again'
267
+ # With each and no block.
268
+ @client.execute(@query1).each
269
+ @client.sqlsent?.must_equal false
270
+ @client.canceled?.must_equal false
271
+ # With each and block.
272
+ @client.execute(@query1).each do |row|
273
+ @client.sqlsent?.must_equal true, 'when iterating over each row in a block'
274
+ @client.canceled?.must_equal false
275
+ end
276
+ @client.sqlsent?.must_equal false
277
+ @client.canceled?.must_equal false
278
+ # With each and block canceled half way thru.
279
+ count = @client.execute("SELECT COUNT([id]) AS [count] FROM [datatypes]").each[0]['count']
280
+ assert count > 10, 'since we want to cancel early for test'
281
+ result = @client.execute("SELECT [id] FROM [datatypes]")
282
+ index = 0
283
+ result.each do |row|
284
+ break if index > 10
285
+ index += 1
286
+ end
287
+ @client.sqlsent?.must_equal true
288
+ @client.canceled?.must_equal false
289
+ result.cancel
290
+ @client.sqlsent?.must_equal false
291
+ @client.canceled?.must_equal true
292
+ # With do method.
293
+ @client.execute(@query1).do
294
+ @client.sqlsent?.must_equal false
295
+ @client.canceled?.must_equal true
296
+ # With insert method.
297
+ rollback_transaction(@client) do
298
+ @client.execute("INSERT INTO [datatypes] ([varchar_50]) VALUES ('test')").insert
299
+ @client.sqlsent?.must_equal false
300
+ @client.canceled?.must_equal true
301
+ end
302
+ # With first
303
+ @client.execute("SELECT [id] FROM [datatypes]").each(:first => true)
304
+ @client.sqlsent?.must_equal false
305
+ @client.canceled?.must_equal true
306
+ end
307
+
308
+ it 'use same string object for hash keys' do
309
+ data = @client.execute("SELECT [id], [bigint] FROM [datatypes]").each
310
+ assert_equal data.first.keys.map{ |r| r.object_id }, data.last.keys.map{ |r| r.object_id }
311
+ end
312
+
313
+ it 'has properly encoded column names with symbol keys' do
314
+ col_name = "öäüß"
315
+ @client.execute("DROP TABLE [test_encoding]").do rescue nil
316
+ @client.execute("CREATE TABLE [dbo].[test_encoding] ( [id] int NOT NULL IDENTITY(1,1) PRIMARY KEY, [#{col_name}] [nvarchar](10) NOT NULL )").do
317
+ @client.execute("INSERT INTO [test_encoding] ([#{col_name}]) VALUES (N'#{col_name}')").do
318
+ result = @client.execute("SELECT [#{col_name}] FROM [test_encoding]")
319
+ row = result.each(:as => :hash, :symbolize_keys => true).first
320
+ assert_instance_of Symbol, result.fields.first
321
+ assert_equal col_name.to_sym, result.fields.first
322
+ assert_instance_of Symbol, row.keys.first
323
+ assert_equal col_name.to_sym, row.keys.first
324
+ end
325
+
326
+ it 'allows #return_code to work with stored procedures and reset per sql batch' do
327
+ assert_nil @client.return_code
328
+ result = @client.execute("EXEC tinytds_TestReturnCodes")
329
+ assert_equal [{"one"=>1}], result.each
330
+ assert_equal 420, @client.return_code
331
+ assert_equal 420, result.return_code
332
+ result = @client.execute('SELECT 1 as [one]')
333
+ result.each
334
+ assert_nil @client.return_code
335
+ assert_nil result.return_code
336
+ end
337
+
338
+ describe 'with multiple result sets' do
339
+
340
+ before do
341
+ @empty_select = "SELECT 1 AS [rs1] WHERE 1 = 0"
342
+ @double_select = "SELECT 1 AS [rs1]
343
+ SELECT 2 AS [rs2]"
344
+ @triple_select_1st_empty = "SELECT 1 AS [rs1] WHERE 1 = 0
345
+ SELECT 2 AS [rs2]
346
+ SELECT 3 AS [rs3]"
347
+ @triple_select_2nd_empty = "SELECT 1 AS [rs1]
348
+ SELECT 2 AS [rs2] WHERE 1 = 0
349
+ SELECT 3 AS [rs3]"
350
+ @triple_select_3rd_empty = "SELECT 1 AS [rs1]
351
+ SELECT 2 AS [rs2]
352
+ SELECT 3 AS [rs3] WHERE 1 = 0"
353
+ end
354
+
355
+ it 'handles a command buffer with double selects' do
356
+ result = @client.execute(@double_select)
357
+ result_sets = result.each
358
+ assert_equal 2, result_sets.size
359
+ assert_equal [{'rs1' => 1}], result_sets.first
360
+ assert_equal [{'rs2' => 2}], result_sets.last
361
+ assert_equal [['rs1'], ['rs2']], result.fields
362
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
363
+ # As array
364
+ result = @client.execute(@double_select)
365
+ result_sets = result.each(:as => :array)
366
+ assert_equal 2, result_sets.size
367
+ assert_equal [[1]], result_sets.first
368
+ assert_equal [[2]], result_sets.last
369
+ assert_equal [['rs1'], ['rs2']], result.fields
370
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
371
+ end
372
+
373
+ it 'yields each row for each result set' do
374
+ data = []
375
+ result_sets = @client.execute(@double_select).each { |row| data << row }
376
+ assert_equal data.first, result_sets.first[0]
377
+ assert_equal data.last, result_sets.last[0]
378
+ end
379
+
380
+ it 'works from a stored procedure' do
381
+ if sqlserver?
382
+ results1, results2 = @client.execute("EXEC sp_helpconstraint '[datatypes]'").each
383
+ assert_equal [{"Object Name"=>"[datatypes]"}], results1
384
+ constraint_info = results2.first
385
+ assert constraint_info.key?("constraint_keys")
386
+ assert constraint_info.key?("constraint_type")
387
+ assert constraint_info.key?("constraint_name")
388
+ elsif sybase_ase?
389
+ results1, results2 = @client.execute("EXEC sp_helpconstraint 'datatypes'").each
390
+ assert results1['name'] =~ /^datatypes_bit/
391
+ assert results1['defintion'] == 'DEFAULT 0'
392
+ assert results2['name'] =~ /^datatypes_id/
393
+ assert results2['defintion'] =~ /^PRIMARY KEY/
394
+ end
395
+ end
396
+
397
+ describe 'using :empty_sets TRUE' do
398
+
399
+ before do
400
+ close_client
401
+ @old_query_option_value = TinyTds::Client.default_query_options[:empty_sets]
402
+ TinyTds::Client.default_query_options[:empty_sets] = true
403
+ @client = new_connection
404
+ end
405
+
406
+ after do
407
+ TinyTds::Client.default_query_options[:empty_sets] = @old_query_option_value
408
+ end
409
+
410
+ it 'handles a basic empty result set' do
411
+ result = @client.execute(@empty_select)
412
+ assert_equal [], result.each
413
+ assert_equal ['rs1'], result.fields
414
+ end
415
+
416
+ it 'includes empty result sets by default - using 1st empty buffer' do
417
+ result = @client.execute(@triple_select_1st_empty)
418
+ result_sets = result.each
419
+ assert_equal 3, result_sets.size
420
+ assert_equal [], result_sets[0]
421
+ assert_equal [{'rs2' => 2}], result_sets[1]
422
+ assert_equal [{'rs3' => 3}], result_sets[2]
423
+ assert_equal [['rs1'], ['rs2'], ['rs3']], result.fields
424
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
425
+ # As array
426
+ result = @client.execute(@triple_select_1st_empty)
427
+ result_sets = result.each(:as => :array)
428
+ assert_equal 3, result_sets.size
429
+ assert_equal [], result_sets[0]
430
+ assert_equal [[2]], result_sets[1]
431
+ assert_equal [[3]], result_sets[2]
432
+ assert_equal [['rs1'], ['rs2'], ['rs3']], result.fields
433
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
434
+ end
435
+
436
+ it 'includes empty result sets by default - using 2nd empty buffer' do
437
+ result = @client.execute(@triple_select_2nd_empty)
438
+ result_sets = result.each
439
+ assert_equal 3, result_sets.size
440
+ assert_equal [{'rs1' => 1}], result_sets[0]
441
+ assert_equal [], result_sets[1]
442
+ assert_equal [{'rs3' => 3}], result_sets[2]
443
+ assert_equal [['rs1'], ['rs2'], ['rs3']], result.fields
444
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
445
+ # As array
446
+ result = @client.execute(@triple_select_2nd_empty)
447
+ result_sets = result.each(:as => :array)
448
+ assert_equal 3, result_sets.size
449
+ assert_equal [[1]], result_sets[0]
450
+ assert_equal [], result_sets[1]
451
+ assert_equal [[3]], result_sets[2]
452
+ assert_equal [['rs1'], ['rs2'], ['rs3']], result.fields
453
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
454
+ end
455
+
456
+ it 'includes empty result sets by default - using 3rd empty buffer' do
457
+ result = @client.execute(@triple_select_3rd_empty)
458
+ result_sets = result.each
459
+ assert_equal 3, result_sets.size
460
+ assert_equal [{'rs1' => 1}], result_sets[0]
461
+ assert_equal [{'rs2' => 2}], result_sets[1]
462
+ assert_equal [], result_sets[2]
463
+ assert_equal [['rs1'], ['rs2'], ['rs3']], result.fields
464
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
465
+ # As array
466
+ result = @client.execute(@triple_select_3rd_empty)
467
+ result_sets = result.each(:as => :array)
468
+ assert_equal 3, result_sets.size
469
+ assert_equal [[1]], result_sets[0]
470
+ assert_equal [[2]], result_sets[1]
471
+ assert_equal [], result_sets[2]
472
+ assert_equal [['rs1'], ['rs2'], ['rs3']], result.fields
473
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
474
+ end
475
+
476
+ end
477
+
478
+ describe 'using :empty_sets FALSE' do
479
+
480
+ before do
481
+ close_client
482
+ @old_query_option_value = TinyTds::Client.default_query_options[:empty_sets]
483
+ TinyTds::Client.default_query_options[:empty_sets] = false
484
+ @client = new_connection
485
+ end
486
+
487
+ after do
488
+ TinyTds::Client.default_query_options[:empty_sets] = @old_query_option_value
489
+ end
490
+
491
+ it 'handles a basic empty result set' do
492
+ result = @client.execute(@empty_select)
493
+ assert_equal [], result.each
494
+ assert_equal ['rs1'], result.fields
495
+ end
496
+
497
+ it 'must not include empty result sets by default - using 1st empty buffer' do
498
+ result = @client.execute(@triple_select_1st_empty)
499
+ result_sets = result.each
500
+ assert_equal 2, result_sets.size
501
+ assert_equal [{'rs2' => 2}], result_sets[0]
502
+ assert_equal [{'rs3' => 3}], result_sets[1]
503
+ assert_equal [['rs2'], ['rs3']], result.fields
504
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
505
+ # As array
506
+ result = @client.execute(@triple_select_1st_empty)
507
+ result_sets = result.each(:as => :array)
508
+ assert_equal 2, result_sets.size
509
+ assert_equal [[2]], result_sets[0]
510
+ assert_equal [[3]], result_sets[1]
511
+ assert_equal [['rs2'], ['rs3']], result.fields
512
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
513
+ end
514
+
515
+ it 'must not include empty result sets by default - using 2nd empty buffer' do
516
+ result = @client.execute(@triple_select_2nd_empty)
517
+ result_sets = result.each
518
+ assert_equal 2, result_sets.size
519
+ assert_equal [{'rs1' => 1}], result_sets[0]
520
+ assert_equal [{'rs3' => 3}], result_sets[1]
521
+ assert_equal [['rs1'], ['rs3']], result.fields
522
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
523
+ # As array
524
+ result = @client.execute(@triple_select_2nd_empty)
525
+ result_sets = result.each(:as => :array)
526
+ assert_equal 2, result_sets.size
527
+ assert_equal [[1]], result_sets[0]
528
+ assert_equal [[3]], result_sets[1]
529
+ assert_equal [['rs1'], ['rs3']], result.fields
530
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
531
+ end
532
+
533
+ it 'must not include empty result sets by default - using 3rd empty buffer' do
534
+ result = @client.execute(@triple_select_3rd_empty)
535
+ result_sets = result.each
536
+ assert_equal 2, result_sets.size
537
+ assert_equal [{'rs1' => 1}], result_sets[0]
538
+ assert_equal [{'rs2' => 2}], result_sets[1]
539
+ assert_equal [['rs1'], ['rs2']], result.fields
540
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
541
+ # As array
542
+ result = @client.execute(@triple_select_3rd_empty)
543
+ result_sets = result.each(:as => :array)
544
+ assert_equal 2, result_sets.size
545
+ assert_equal [[1]], result_sets[0]
546
+ assert_equal [[2]], result_sets[1]
547
+ assert_equal [['rs1'], ['rs2']], result.fields
548
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
549
+ end
550
+
551
+ end
552
+
553
+ end
554
+
555
+ describe 'Complex query with multiple results sets but no actual results' do
556
+
557
+ let(:backup_file) { 'C:\\Users\\Public\\tinytdstest.bak' }
558
+
559
+ after { File.delete(backup_file) if File.exist?(backup_file) }
560
+
561
+ it 'must not cancel the query until complete' do
562
+ @client.execute("BACKUP DATABASE tinytdstest TO DISK = '#{backup_file}'").do
563
+ end
564
+
565
+ end unless sqlserver_azure?
566
+
567
+ describe 'when casting to native ruby values' do
568
+
569
+ it 'returns fixnum for 1' do
570
+ value = @client.execute('SELECT 1 AS [fixnum]').each.first['fixnum']
571
+ assert_equal 1, value
572
+ end
573
+
574
+ it 'returns nil for NULL' do
575
+ value = @client.execute('SELECT NULL AS [null]').each.first['null']
576
+ assert_equal nil, value
577
+ end
578
+
579
+ end
580
+
581
+ describe 'with data type' do
582
+
583
+ describe 'char max' do
584
+
585
+ before do
586
+ @big_text = 'x' * 2_000_000
587
+ @old_textsize = @client.execute("SELECT @@TEXTSIZE AS [textsize]").each.first['textsize'].inspect
588
+ @client.execute("SET TEXTSIZE #{(@big_text.length*2)+1}").do
589
+ end
590
+
591
+ it 'must insert and select large varchar_max' do
592
+ insert_and_select_datatype :varchar_max
593
+ end
594
+
595
+ it 'must insert and select large nvarchar_max' do
596
+ insert_and_select_datatype :nvarchar_max
597
+ end
598
+
599
+ end unless sqlserver_2000? || sybase_ase?
600
+
601
+ end
602
+
603
+ describe 'when shit happens' do
604
+
605
+ it 'copes with nil or empty buffer' do
606
+ assert_raises(TypeError) { @client.execute(nil) }
607
+ assert_equal [], @client.execute('').each
608
+ end
609
+
610
+ if sqlserver?
611
+
612
+ it 'must not raise an error when severity is 10 or less' do
613
+ (1..10).to_a.each do |severity|
614
+ @client.execute("RAISERROR(N'Test #{severity} severity', #{severity}, 1)").do
615
+ end
616
+ end
617
+
618
+ it 'raises an error when severity is greater than 10' do
619
+ action = lambda { @client.execute("RAISERROR(N'Test 11 severity', 11, 1)").do }
620
+ assert_raise_tinytds_error(action) do |e|
621
+ assert_equal "Test 11 severity", e.message
622
+ assert_equal 11, e.severity
623
+ assert_equal 50000, e.db_error_number
624
+ end
625
+ end
626
+
627
+ else
628
+
629
+ it 'raises an error' do
630
+ action = lambda { @client.execute("RAISERROR 50000 N'Hello World'").do }
631
+ assert_raise_tinytds_error(action) do |e|
632
+ assert_equal "Hello World", e.message
633
+ assert_equal 16, e.severity # predefined on ASE
634
+ assert_equal 50000, e.db_error_number
635
+ end
636
+ assert_followup_query
637
+ end
638
+
639
+ end
640
+
641
+ it 'throws an error when you execute another query with other results pending' do
642
+ result1 = @client.execute(@query1)
643
+ action = lambda { @client.execute(@query1) }
644
+ assert_raise_tinytds_error(action) do |e|
645
+ assert_match %r|with results pending|i, e.message
646
+ assert_equal 7, e.severity
647
+ assert_equal 20019, e.db_error_number
648
+ end
649
+ end
650
+
651
+ it 'must error gracefully with bad table name' do
652
+ action = lambda { @client.execute('SELECT * FROM [foobar]').each }
653
+ assert_raise_tinytds_error(action) do |e|
654
+ pattern = sybase_ase? ? /foobar not found/ : %r|invalid object name.*foobar|i
655
+ assert_match pattern, e.message
656
+ assert_equal 16, e.severity
657
+ assert_equal 208, e.db_error_number
658
+ end
659
+ assert_followup_query
660
+ end
661
+
662
+ it 'must error gracefully with incorrect syntax' do
663
+ action = lambda { @client.execute('this will not work').each }
664
+ assert_raise_tinytds_error(action) do |e|
665
+ assert_match %r|incorrect syntax|i, e.message
666
+ assert_equal 15, e.severity
667
+ assert_equal 156, e.db_error_number
668
+ end
669
+ assert_followup_query
670
+ end
671
+
672
+ it 'must not error at all from reading non-convertable charcters and just use ? marks' do
673
+ close_client
674
+ @client = new_connection :encoding => 'ASCII'
675
+ @client.charset.must_equal 'ASCII'
676
+ find_value(202, :nvarchar_50).must_equal 'test nvarchar_50 ??'
677
+ end
678
+
679
+ it 'must error gracefully from writing non-convertable characters' do
680
+ close_client
681
+ @client = new_connection :encoding => 'ASCII'
682
+ @client.charset.must_equal 'ASCII'
683
+ rollback_transaction(@client) do
684
+ text = 'Test ✓'
685
+ @client.execute("DELETE FROM [datatypes] WHERE [nvarchar_50] IS NOT NULL").do
686
+ action = lambda { @client.execute("INSERT INTO [datatypes] ([nvarchar_50]) VALUES ('#{text}')").do }
687
+ assert_raise_tinytds_error(action) do |e|
688
+ e.message.must_match %r{Unclosed quotation mark}i
689
+ e.severity.must_equal 15
690
+ e.db_error_number.must_equal 105
691
+ end
692
+ assert_followup_query
693
+ end
694
+ end
695
+
696
+ it 'errors gracefully with incorrect syntax in sp_executesql' do
697
+ action = lambda { @client.execute("EXEC sp_executesql N'this will not work'").each }
698
+ assert_raise_tinytds_error(action) do |e|
699
+ assert_match %r|incorrect syntax|i, e.message
700
+ assert_equal 15, e.severity
701
+ assert_equal 156, e.db_error_number
702
+ end
703
+ assert_followup_query
704
+ end unless sybase_ase?
705
+
706
+ end
707
+
708
+ end
709
+
710
+
711
+ protected
712
+
713
+ def assert_followup_query
714
+ result = @client.execute(@query1)
715
+ assert_equal 1, result.each.first['one']
716
+ end
717
+
718
+ def insert_and_select_datatype(datatype)
719
+ rollback_transaction(@client) do
720
+ @client.execute("DELETE FROM [datatypes] WHERE [#{datatype}] IS NOT NULL").do
721
+ id = @client.execute("INSERT INTO [datatypes] ([#{datatype}]) VALUES (N'#{@big_text}')").insert
722
+ found_text = find_value id, datatype
723
+ flunk "Large #{datatype} data with a length of #{@big_text.length} did not match found text with length of #{found_text.length}" unless @big_text == found_text
724
+ end
725
+ end
726
+
727
+ end
728
+