tiny_tds 2.1.2 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ci.yml +470 -0
  3. data/.gitignore +2 -0
  4. data/CHANGELOG.md +39 -1
  5. data/Gemfile +0 -7
  6. data/ISSUE_TEMPLATE.md +1 -1
  7. data/README.md +50 -59
  8. data/Rakefile +15 -10
  9. data/VERSION +1 -1
  10. data/docker-compose.yml +34 -0
  11. data/ext/tiny_tds/client.c +100 -59
  12. data/ext/tiny_tds/client.h +5 -3
  13. data/ext/tiny_tds/extconf.rb +38 -16
  14. data/ext/tiny_tds/extconsts.rb +4 -10
  15. data/ext/tiny_tds/result.c +52 -45
  16. data/ext/tiny_tds/tiny_tds_ext.c +4 -1
  17. data/lib/tiny_tds/gem.rb +1 -6
  18. data/setup_cimgruby_dev.sh +25 -0
  19. data/start_dev.sh +21 -0
  20. data/tasks/native_gem.rake +15 -6
  21. data/tasks/ports/freetds.rb +1 -6
  22. data/tasks/ports/libiconv.rb +0 -17
  23. data/tasks/ports/openssl.rb +2 -18
  24. data/tasks/ports/recipe.rb +16 -4
  25. data/tasks/ports.rake +61 -40
  26. data/test/bin/install-mssql.ps1 +42 -0
  27. data/test/bin/install-mssqltools.sh +9 -0
  28. data/test/bin/setup_tinytds_db.sh +7 -0
  29. data/test/bin/setup_volume_permissions.sh +10 -0
  30. data/test/client_test.rb +101 -59
  31. data/test/gem_test.rb +25 -28
  32. data/test/result_test.rb +130 -182
  33. data/test/schema_test.rb +366 -388
  34. data/test/sql/db-create.sql +18 -0
  35. data/test/sql/db-login.sql +38 -0
  36. data/test/test_helper.rb +63 -31
  37. data/test/thread_test.rb +1 -1
  38. data/tiny_tds.gemspec +10 -7
  39. metadata +70 -52
  40. data/.travis.yml +0 -24
  41. data/BACKERS.md +0 -32
  42. data/appveyor.yml +0 -51
  43. data/test/appveyor/dbsetup.ps1 +0 -27
  44. data/test/appveyor/dbsetup.sql +0 -9
  45. data/test/bin/setup.sh +0 -19
  46. data/test/schema/sqlserver_2000.sql +0 -140
  47. data/test/schema/sqlserver_2005.sql +0 -140
  48. data/test/schema/sqlserver_2014.sql +0 -140
  49. data/test/schema/sqlserver_2016.sql +0 -140
  50. data/test/schema/sybase_ase.sql +0 -138
  51. /data/test/schema/{sqlserver_2008.sql → sqlserver_2017.sql} +0 -0
data/test/result_test.rb CHANGED
@@ -80,7 +80,7 @@ class ResultTest < TinyTds::TestCase
80
80
  @client.execute("DELETE FROM [datatypes]").do
81
81
  @client.execute("INSERT INTO [datatypes] ([char_10], [varchar_50]) VALUES ('1', '2')").do
82
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"
83
+ _(result).must_equal "1 test2"
84
84
  end
85
85
  end
86
86
 
@@ -135,10 +135,10 @@ class ResultTest < TinyTds::TestCase
135
135
  text = 'test affected rows sql'
136
136
  @client.execute("DELETE FROM [datatypes]").do
137
137
  afrows = @client.execute("SELECT @@ROWCOUNT AS AffectedRows").each.first['AffectedRows']
138
- ['Fixnum', 'Integer'].must_include afrows.class.name
138
+ _(['Fixnum', 'Integer']).must_include afrows.class.name
139
139
  @client.execute("INSERT INTO [datatypes] ([varchar_50]) VALUES ('#{text}')").do
140
140
  pk1 = @client.execute(@client.identity_sql).each.first['Ident']
141
- ['Fixnum', 'Integer'].must_include pk1.class.name, 'we it be able to CAST to bigint'
141
+ _(['Fixnum', 'Integer']).must_include pk1.class.name, 'we it be able to CAST to bigint'
142
142
  @client.execute("UPDATE [datatypes] SET [varchar_50] = NULL WHERE [varchar_50] = '#{text}'").do
143
143
  afrows = @client.execute("SELECT @@ROWCOUNT AS AffectedRows").each.first['AffectedRows']
144
144
  assert_equal 1, afrows
@@ -154,7 +154,7 @@ class ResultTest < TinyTds::TestCase
154
154
  inserted_rows = @client.execute("INSERT INTO [datatypes] ([varchar_50]) VALUES ('#{text}')").do
155
155
  assert_equal 1, inserted_rows, 'should have inserted row for one above'
156
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
157
+ assert_equal 1, updated_rows, 'should have updated row for one above'
158
158
  end
159
159
  end
160
160
 
@@ -166,7 +166,7 @@ class ResultTest < TinyTds::TestCase
166
166
  inserted_rows = @client.execute("INSERT INTO [datatypes] ([varchar_50]) VALUES ('#{text}')").do
167
167
  assert_equal 1, inserted_rows, 'should have inserted row for one above'
168
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
169
+ assert_equal 1, updated_rows, 'should have updated row for one above'
170
170
  end
171
171
  end
172
172
 
@@ -177,19 +177,18 @@ class ResultTest < TinyTds::TestCase
177
177
  @client.execute("INSERT INTO [datatypes] ([varchar_50]) VALUES ('#{text}')").do
178
178
  sql_identity = @client.execute(@client.identity_sql).each.first['Ident']
179
179
  native_identity = @client.execute("INSERT INTO [datatypes] ([varchar_50]) VALUES ('#{text}')").insert
180
- assert_equal sql_identity+1, native_identity
180
+ assert_equal sql_identity + 1, native_identity
181
181
  end
182
182
  end
183
183
 
184
184
  it 'returns bigint for #insert when needed' do
185
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.
186
+ # 'CREATE TABLE' command is not allowed within a multi-statement transaction
187
+ # and and sp_helpindex creates a temporary table #spindtab.
189
188
  rollback_transaction(@client) do
190
189
  seed = 9223372036854775805
191
190
  @client.execute("DELETE FROM [datatypes]").do
192
- id_constraint_name = @client.execute("EXEC sp_helpindex [datatypes]").detect{ |row| row['index_keys'] == 'id' }['index_name']
191
+ id_constraint_name = @client.execute("EXEC sp_helpindex [datatypes]").detect { |row| row['index_keys'] == 'id' }['index_name']
193
192
  @client.execute("ALTER TABLE [datatypes] DROP CONSTRAINT [#{id_constraint_name}]").do
194
193
  @client.execute("ALTER TABLE [datatypes] DROP COLUMN [id]").do
195
194
  @client.execute("ALTER TABLE [datatypes] ADD [id] [bigint] NOT NULL IDENTITY(1,1) PRIMARY KEY").do
@@ -215,34 +214,34 @@ class ResultTest < TinyTds::TestCase
215
214
  @client.execute("DELETE FROM [datatypes]").do
216
215
  @client.execute("ROLLBACK TRANSACTION").do
217
216
  count = @client.execute("SELECT COUNT(*) AS [count] FROM [datatypes]").each.first['count']
218
- 0.wont_equal count
217
+ _(count).wont_equal 0
219
218
  end
220
219
 
221
220
  it 'has a #fields accessor with logic default and valid outcome' do
222
221
  result = @client.execute(@query1)
223
- result.fields.must_equal ['one']
222
+ _(result.fields).must_equal ['one']
224
223
  result.each
225
- result.fields.must_equal ['one']
224
+ _(result.fields).must_equal ['one']
226
225
  end
227
226
 
228
227
  it 'always returns an array for fields for all sql' do
229
228
  result = @client.execute("USE [tinytdstest]")
230
- result.fields.must_equal []
229
+ _(result.fields).must_equal []
231
230
  result.do
232
- result.fields.must_equal []
231
+ _(result.fields).must_equal []
233
232
  end
234
233
 
235
234
  it 'returns fields even when no results are found' do
236
235
  no_results_query = "SELECT [id], [varchar_50] FROM [datatypes] WHERE [varchar_50] = 'NOTFOUND'"
237
236
  # Fields before each.
238
237
  result = @client.execute(no_results_query)
239
- result.fields.must_equal ['id','varchar_50']
238
+ _(result.fields).must_equal ['id', 'varchar_50']
240
239
  result.each
241
- result.fields.must_equal ['id','varchar_50']
240
+ _(result.fields).must_equal ['id', 'varchar_50']
242
241
  # Each then fields
243
242
  result = @client.execute(no_results_query)
244
243
  result.each
245
- result.fields.must_equal ['id','varchar_50']
244
+ _(result.fields).must_equal ['id', 'varchar_50']
246
245
  end
247
246
 
248
247
  it 'allows the result to be canceled before reading' do
@@ -254,27 +253,27 @@ class ResultTest < TinyTds::TestCase
254
253
  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
254
  # Default state.
256
255
  @client = TinyTds::Client.new(connection_options)
257
- @client.sqlsent?.must_equal false
258
- @client.canceled?.must_equal false
256
+ _(@client.sqlsent?).must_equal false
257
+ _(@client.canceled?).must_equal false
259
258
  # With active result before and after cancel.
260
259
  result = @client.execute(@query1)
261
- @client.sqlsent?.must_equal true
262
- @client.canceled?.must_equal false
260
+ _(@client.sqlsent?).must_equal true
261
+ _(@client.canceled?).must_equal false
263
262
  result.cancel
264
- @client.sqlsent?.must_equal false
265
- @client.canceled?.must_equal true
263
+ _(@client.sqlsent?).must_equal false
264
+ _(@client.canceled?).must_equal true
266
265
  assert result.cancel, 'must be safe to call again'
267
266
  # With each and no block.
268
267
  @client.execute(@query1).each
269
- @client.sqlsent?.must_equal false
270
- @client.canceled?.must_equal false
268
+ _(@client.sqlsent?).must_equal false
269
+ _(@client.canceled?).must_equal false
271
270
  # With each and block.
272
271
  @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
272
+ _(@client.sqlsent?).must_equal true, 'when iterating over each row in a block'
273
+ _(@client.canceled?).must_equal false
275
274
  end
276
- @client.sqlsent?.must_equal false
277
- @client.canceled?.must_equal false
275
+ _(@client.sqlsent?).must_equal false
276
+ _(@client.canceled?).must_equal false
278
277
  # With each and block canceled half way thru.
279
278
  count = @client.execute("SELECT COUNT([id]) AS [count] FROM [datatypes]").each[0]['count']
280
279
  assert count > 10, 'since we want to cancel early for test'
@@ -284,30 +283,30 @@ class ResultTest < TinyTds::TestCase
284
283
  break if index > 10
285
284
  index += 1
286
285
  end
287
- @client.sqlsent?.must_equal true
288
- @client.canceled?.must_equal false
286
+ _(@client.sqlsent?).must_equal true
287
+ _(@client.canceled?).must_equal false
289
288
  result.cancel
290
- @client.sqlsent?.must_equal false
291
- @client.canceled?.must_equal true
289
+ _(@client.sqlsent?).must_equal false
290
+ _(@client.canceled?).must_equal true
292
291
  # With do method.
293
292
  @client.execute(@query1).do
294
- @client.sqlsent?.must_equal false
295
- @client.canceled?.must_equal true
293
+ _(@client.sqlsent?).must_equal false
294
+ _(@client.canceled?).must_equal true
296
295
  # With insert method.
297
296
  rollback_transaction(@client) do
298
297
  @client.execute("INSERT INTO [datatypes] ([varchar_50]) VALUES ('test')").insert
299
- @client.sqlsent?.must_equal false
300
- @client.canceled?.must_equal true
298
+ _(@client.sqlsent?).must_equal false
299
+ _(@client.canceled?).must_equal true
301
300
  end
302
301
  # With first
303
302
  @client.execute("SELECT [id] FROM [datatypes]").each(:first => true)
304
- @client.sqlsent?.must_equal false
305
- @client.canceled?.must_equal true
303
+ _(@client.sqlsent?).must_equal false
304
+ _(@client.canceled?).must_equal true
306
305
  end
307
306
 
308
307
  it 'use same string object for hash keys' do
309
308
  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 }
309
+ assert_equal data.first.keys.map { |r| r.object_id }, data.last.keys.map { |r| r.object_id }
311
310
  end
312
311
 
313
312
  it 'has properly encoded column names with symbol keys' do
@@ -326,7 +325,7 @@ class ResultTest < TinyTds::TestCase
326
325
  it 'allows #return_code to work with stored procedures and reset per sql batch' do
327
326
  assert_nil @client.return_code
328
327
  result = @client.execute("EXEC tinytds_TestReturnCodes")
329
- assert_equal [{"one"=>1}], result.each
328
+ assert_equal [{ "one" => 1 }], result.each
330
329
  assert_equal 420, @client.return_code
331
330
  assert_equal 420, result.return_code
332
331
  result = @client.execute('SELECT 1 as [one]')
@@ -337,13 +336,13 @@ class ResultTest < TinyTds::TestCase
337
336
 
338
337
  it 'with LOGINPROPERTY function' do
339
338
  v = @client.execute("SELECT LOGINPROPERTY('sa', 'IsLocked') as v").first['v']
340
- v.must_equal 0
339
+ _(v).must_equal 0
341
340
  end
342
341
 
343
342
  describe 'with multiple result sets' do
344
343
 
345
344
  before do
346
- @empty_select = "SELECT 1 AS [rs1] WHERE 1 = 0"
345
+ @empty_select = "SELECT 1 AS [rs1] WHERE 1 = 0"
347
346
  @double_select = "SELECT 1 AS [rs1]
348
347
  SELECT 2 AS [rs2]"
349
348
  @triple_select_1st_empty = "SELECT 1 AS [rs1] WHERE 1 = 0
@@ -361,8 +360,8 @@ class ResultTest < TinyTds::TestCase
361
360
  result = @client.execute(@double_select)
362
361
  result_sets = result.each
363
362
  assert_equal 2, result_sets.size
364
- assert_equal [{'rs1' => 1}], result_sets.first
365
- assert_equal [{'rs2' => 2}], result_sets.last
363
+ assert_equal [{ 'rs1' => 1 }], result_sets.first
364
+ assert_equal [{ 'rs2' => 2 }], result_sets.last
366
365
  assert_equal [['rs1'], ['rs2']], result.fields
367
366
  assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
368
367
  # As array
@@ -383,20 +382,12 @@ class ResultTest < TinyTds::TestCase
383
382
  end
384
383
 
385
384
  it 'works from a stored procedure' do
386
- if sqlserver?
387
- results1, results2 = @client.execute("EXEC sp_helpconstraint '[datatypes]'").each
388
- assert_equal [{"Object Name"=>"[datatypes]"}], results1
389
- constraint_info = results2.first
390
- assert constraint_info.key?("constraint_keys")
391
- assert constraint_info.key?("constraint_type")
392
- assert constraint_info.key?("constraint_name")
393
- elsif sybase_ase?
394
- results1, results2 = @client.execute("EXEC sp_helpconstraint 'datatypes'").each
395
- assert results1['name'] =~ /^datatypes_bit/
396
- assert results1['defintion'] == 'DEFAULT 0'
397
- assert results2['name'] =~ /^datatypes_id/
398
- assert results2['defintion'] =~ /^PRIMARY KEY/
399
- end
385
+ results1, results2 = @client.execute("EXEC sp_helpconstraint '[datatypes]'").each
386
+ assert_equal [{ "Object Name" => "[datatypes]" }], results1
387
+ constraint_info = results2.first
388
+ assert constraint_info.key?("constraint_keys")
389
+ assert constraint_info.key?("constraint_type")
390
+ assert constraint_info.key?("constraint_name")
400
391
  end
401
392
 
402
393
  describe 'using :empty_sets TRUE' do
@@ -423,8 +414,8 @@ class ResultTest < TinyTds::TestCase
423
414
  result_sets = result.each
424
415
  assert_equal 3, result_sets.size
425
416
  assert_equal [], result_sets[0]
426
- assert_equal [{'rs2' => 2}], result_sets[1]
427
- assert_equal [{'rs3' => 3}], result_sets[2]
417
+ assert_equal [{ 'rs2' => 2 }], result_sets[1]
418
+ assert_equal [{ 'rs3' => 3 }], result_sets[2]
428
419
  assert_equal [['rs1'], ['rs2'], ['rs3']], result.fields
429
420
  assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
430
421
  # As array
@@ -442,9 +433,9 @@ class ResultTest < TinyTds::TestCase
442
433
  result = @client.execute(@triple_select_2nd_empty)
443
434
  result_sets = result.each
444
435
  assert_equal 3, result_sets.size
445
- assert_equal [{'rs1' => 1}], result_sets[0]
436
+ assert_equal [{ 'rs1' => 1 }], result_sets[0]
446
437
  assert_equal [], result_sets[1]
447
- assert_equal [{'rs3' => 3}], result_sets[2]
438
+ assert_equal [{ 'rs3' => 3 }], result_sets[2]
448
439
  assert_equal [['rs1'], ['rs2'], ['rs3']], result.fields
449
440
  assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
450
441
  # As array
@@ -462,8 +453,8 @@ class ResultTest < TinyTds::TestCase
462
453
  result = @client.execute(@triple_select_3rd_empty)
463
454
  result_sets = result.each
464
455
  assert_equal 3, result_sets.size
465
- assert_equal [{'rs1' => 1}], result_sets[0]
466
- assert_equal [{'rs2' => 2}], result_sets[1]
456
+ assert_equal [{ 'rs1' => 1 }], result_sets[0]
457
+ assert_equal [{ 'rs2' => 2 }], result_sets[1]
467
458
  assert_equal [], result_sets[2]
468
459
  assert_equal [['rs1'], ['rs2'], ['rs3']], result.fields
469
460
  assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
@@ -503,8 +494,8 @@ class ResultTest < TinyTds::TestCase
503
494
  result = @client.execute(@triple_select_1st_empty)
504
495
  result_sets = result.each
505
496
  assert_equal 2, result_sets.size
506
- assert_equal [{'rs2' => 2}], result_sets[0]
507
- assert_equal [{'rs3' => 3}], result_sets[1]
497
+ assert_equal [{ 'rs2' => 2 }], result_sets[0]
498
+ assert_equal [{ 'rs3' => 3 }], result_sets[1]
508
499
  assert_equal [['rs2'], ['rs3']], result.fields
509
500
  assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
510
501
  # As array
@@ -521,8 +512,8 @@ class ResultTest < TinyTds::TestCase
521
512
  result = @client.execute(@triple_select_2nd_empty)
522
513
  result_sets = result.each
523
514
  assert_equal 2, result_sets.size
524
- assert_equal [{'rs1' => 1}], result_sets[0]
525
- assert_equal [{'rs3' => 3}], result_sets[1]
515
+ assert_equal [{ 'rs1' => 1 }], result_sets[0]
516
+ assert_equal [{ 'rs3' => 3 }], result_sets[1]
526
517
  assert_equal [['rs1'], ['rs3']], result.fields
527
518
  assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
528
519
  # As array
@@ -539,8 +530,8 @@ class ResultTest < TinyTds::TestCase
539
530
  result = @client.execute(@triple_select_3rd_empty)
540
531
  result_sets = result.each
541
532
  assert_equal 2, result_sets.size
542
- assert_equal [{'rs1' => 1}], result_sets[0]
543
- assert_equal [{'rs2' => 2}], result_sets[1]
533
+ assert_equal [{ 'rs1' => 1 }], result_sets[0]
534
+ assert_equal [{ 'rs2' => 2 }], result_sets[1]
544
535
  assert_equal [['rs1'], ['rs2']], result.fields
545
536
  assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
546
537
  # As array
@@ -590,7 +581,7 @@ class ResultTest < TinyTds::TestCase
590
581
  before do
591
582
  @big_text = 'x' * 2_000_000
592
583
  @old_textsize = @client.execute("SELECT @@TEXTSIZE AS [textsize]").each.first['textsize'].inspect
593
- @client.execute("SET TEXTSIZE #{(@big_text.length*2)+1}").do
584
+ @client.execute("SET TEXTSIZE #{(@big_text.length * 2) + 1}").do
594
585
  end
595
586
 
596
587
  it 'must insert and select large varchar_max' do
@@ -601,7 +592,7 @@ class ResultTest < TinyTds::TestCase
601
592
  insert_and_select_datatype :nvarchar_max
602
593
  end
603
594
 
604
- end unless sqlserver_2000? || sybase_ase?
595
+ end
605
596
 
606
597
  end
607
598
 
@@ -612,147 +603,105 @@ class ResultTest < TinyTds::TestCase
612
603
  assert_equal [], @client.execute('').each
613
604
  end
614
605
 
615
- if sqlserver?
616
-
617
- describe 'using :message_handler option' do
618
- let(:messages) { Array.new }
606
+ describe 'using :message_handler option' do
607
+ let(:messages) { Array.new }
619
608
 
620
- before do
621
- close_client
622
- @client = new_connection message_handler: Proc.new { |m| messages << m }
623
- end
624
-
625
- after do
626
- messages.clear
627
- end
609
+ before do
610
+ close_client
611
+ @client = new_connection message_handler: Proc.new { |m| messages << m }
612
+ end
628
613
 
629
- it 'has a message handler that responds to call' do
630
- assert @client.message_handler.respond_to?(:call)
631
- end
614
+ after do
615
+ messages.clear
616
+ end
632
617
 
633
- it 'calls the provided message handler when severity is 10 or less' do
634
- (1..10).to_a.each do |severity|
635
- messages.clear
636
- msg = "Test #{severity} severity"
637
- state = rand(1..255)
638
- @client.execute("RAISERROR(N'#{msg}', #{severity}, #{state})").do
639
- m = messages.first
640
- assert_equal 1, messages.length, 'there should be one message after one raiserror'
641
- assert_equal msg, m.message, 'message text'
642
- assert_equal severity, m.severity, 'message severity' unless severity == 10 && m.severity.to_i == 0
643
- assert_equal state, m.os_error_number, 'message state'
644
- end
645
- end
618
+ it 'has a message handler that responds to call' do
619
+ assert @client.message_handler.respond_to?(:call)
620
+ end
646
621
 
647
- it 'calls the provided message handler for `print` messages' do
622
+ it 'calls the provided message handler when severity is 10 or less' do
623
+ (1..10).to_a.each do |severity|
648
624
  messages.clear
649
- msg = 'hello'
650
- @client.execute("PRINT '#{msg}'").do
625
+ msg = "Test #{severity} severity"
626
+ state = rand(1..255)
627
+ @client.execute("RAISERROR(N'#{msg}', #{severity}, #{state})").do
651
628
  m = messages.first
652
- assert_equal 1, messages.length, 'there should be one message after one print statement'
629
+ assert_equal 1, messages.length, 'there should be one message after one raiserror'
653
630
  assert_equal msg, m.message, 'message text'
631
+ assert_equal severity, m.severity, 'message severity' unless severity == 10 && m.severity.to_i == 0
632
+ assert_equal state, m.os_error_number, 'message state'
654
633
  end
655
634
  end
656
635
 
657
- it 'must not raise an error when severity is 10 or less' do
658
- (1..10).to_a.each do |severity|
659
- @client.execute("RAISERROR(N'Test #{severity} severity', #{severity}, 1)").do
660
- end
636
+ it 'calls the provided message handler for `print` messages' do
637
+ messages.clear
638
+ msg = 'hello'
639
+ @client.execute("PRINT '#{msg}'").do
640
+ m = messages.first
641
+ assert_equal 1, messages.length, 'there should be one message after one print statement'
642
+ assert_equal msg, m.message, 'message text'
661
643
  end
662
644
 
663
- it 'raises an error when severity is greater than 10' do
664
- action = lambda { @client.execute("RAISERROR(N'Test 11 severity', 11, 1)").do }
645
+ it 'must raise an error preceded by a `print` message' do
646
+ messages.clear
647
+ action = lambda { @client.execute("EXEC tinytds_TestPrintWithError").do }
665
648
  assert_raise_tinytds_error(action) do |e|
666
- assert_equal "Test 11 severity", e.message
667
- assert_equal 11, e.severity
649
+ assert_equal 'hello', messages.first.message, 'message text'
650
+
651
+ assert_equal "Error following print", e.message
652
+ assert_equal 16, e.severity
668
653
  assert_equal 50000, e.db_error_number
669
654
  end
670
655
  end
671
656
 
672
- else
657
+ it 'calls the provided message handler for each of a series of `print` messages' do
658
+ messages.clear
659
+ @client.execute("EXEC tinytds_TestSeveralPrints").do
660
+ assert_equal ['hello 1', 'hello 2', 'hello 3'], messages.map { |e| e.message }, 'message list'
661
+ end
673
662
 
674
- it 'raises an error' do
675
- action = lambda { @client.execute("RAISERROR 50000 N'Hello World'").do }
663
+ it 'should flush info messages before raising error in cases of timeout' do
664
+ @client = new_connection timeout: 1, message_handler: Proc.new { |m| messages << m }
665
+ action = lambda { @client.execute("print 'hello'; waitfor delay '00:00:02'").do }
666
+ messages.clear
676
667
  assert_raise_tinytds_error(action) do |e|
677
- assert_equal "Hello World", e.message
678
- assert_equal 16, e.severity # predefined on ASE
679
- assert_equal 50000, e.db_error_number
668
+ assert_match %r{timed out}i, e.message, 'ignore if non-english test run'
669
+ assert_equal 6, e.severity
670
+ assert_equal 20003, e.db_error_number
671
+ assert_equal 'hello', messages.first&.message, 'message text'
680
672
  end
681
- assert_followup_query
682
673
  end
683
674
 
684
- end
685
-
686
- it 'throws an error when you execute another query with other results pending' do
687
- result1 = @client.execute(@query1)
688
- action = lambda { @client.execute(@query1) }
689
- assert_raise_tinytds_error(action) do |e|
690
- assert_match %r|with results pending|i, e.message
691
- assert_equal 7, e.severity
692
- assert_equal 20019, e.db_error_number
675
+ it 'should print info messages before raising error in cases of timeout' do
676
+ @client = new_connection timeout: 1, message_handler: Proc.new { |m| messages << m }
677
+ action = lambda { @client.execute("raiserror('hello', 1, 1) with nowait; waitfor delay '00:00:02'").do }
678
+ messages.clear
679
+ assert_raise_tinytds_error(action) do |e|
680
+ assert_match %r{timed out}i, e.message, 'ignore if non-english test run'
681
+ assert_equal 6, e.severity
682
+ assert_equal 20003, e.db_error_number
683
+ assert_equal 'hello', messages.first&.message, 'message text'
684
+ end
693
685
  end
694
686
  end
695
687
 
696
- it 'must error gracefully with bad table name' do
697
- action = lambda { @client.execute('SELECT * FROM [foobar]').each }
698
- assert_raise_tinytds_error(action) do |e|
699
- pattern = sybase_ase? ? /foobar not found/ : %r|invalid object name.*foobar|i
700
- assert_match pattern, e.message
701
- assert_equal 16, e.severity
702
- assert_equal 208, e.db_error_number
688
+ it 'must not raise an error when severity is 10 or less' do
689
+ (1..10).to_a.each do |severity|
690
+ @client.execute("RAISERROR(N'Test #{severity} severity', #{severity}, 1)").do
703
691
  end
704
- assert_followup_query
705
692
  end
706
693
 
707
- it 'must error gracefully with incorrect syntax' do
708
- action = lambda { @client.execute('this will not work').each }
694
+ it 'raises an error when severity is greater than 10' do
695
+ action = lambda { @client.execute("RAISERROR(N'Test 11 severity', 11, 1)").do }
709
696
  assert_raise_tinytds_error(action) do |e|
710
- assert_match %r|incorrect syntax|i, e.message
711
- assert_equal 15, e.severity
712
- assert_equal 156, e.db_error_number
713
- end
714
- assert_followup_query
715
- end
716
-
717
- it 'must not error at all from reading non-convertable charcters and just use ? marks' do
718
- close_client
719
- @client = new_connection :encoding => 'ASCII'
720
- @client.charset.must_equal 'ASCII'
721
- find_value(202, :nvarchar_50).must_equal 'test nvarchar_50 ??'
722
- end
723
-
724
- it 'must error gracefully from writing non-convertable characters' do
725
- close_client
726
- @client = new_connection :encoding => 'ASCII'
727
- @client.charset.must_equal 'ASCII'
728
- rollback_transaction(@client) do
729
- text = 'Test ✓'
730
- @client.execute("DELETE FROM [datatypes] WHERE [nvarchar_50] IS NOT NULL").do
731
- action = lambda { @client.execute("INSERT INTO [datatypes] ([nvarchar_50]) VALUES ('#{text}')").do }
732
- assert_raise_tinytds_error(action) do |e|
733
- e.message.must_match %r{Unclosed quotation mark}i
734
- e.severity.must_equal 15
735
- e.db_error_number.must_equal 105
736
- end
737
- assert_followup_query
697
+ assert_equal "Test 11 severity", e.message
698
+ assert_equal 11, e.severity
699
+ assert_equal 50000, e.db_error_number
738
700
  end
739
701
  end
740
-
741
- it 'errors gracefully with incorrect syntax in sp_executesql' do
742
- action = lambda { @client.execute("EXEC sp_executesql N'this will not work'").each }
743
- assert_raise_tinytds_error(action) do |e|
744
- assert_match %r|incorrect syntax|i, e.message
745
- assert_equal 15, e.severity
746
- assert_equal 156, e.db_error_number
747
- end
748
- assert_followup_query
749
- end unless sybase_ase?
750
-
751
702
  end
752
-
753
703
  end
754
704
 
755
-
756
705
  protected
757
706
 
758
707
  def assert_followup_query
@@ -770,4 +719,3 @@ class ResultTest < TinyTds::TestCase
770
719
  end
771
720
 
772
721
  end
773
-