tiny_tds 1.0.4 → 3.2.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 (64) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +20 -0
  3. data/.gitattributes +1 -0
  4. data/.github/workflows/ci.yml +590 -0
  5. data/.gitignore +2 -0
  6. data/.rubocop.yml +31 -0
  7. data/{CHANGELOG → CHANGELOG.md} +133 -26
  8. data/Gemfile +1 -5
  9. data/ISSUE_TEMPLATE.md +36 -3
  10. data/README.md +147 -85
  11. data/Rakefile +51 -94
  12. data/VERSION +1 -1
  13. data/docker-compose.yml +34 -0
  14. data/ext/tiny_tds/client.c +149 -67
  15. data/ext/tiny_tds/client.h +11 -5
  16. data/ext/tiny_tds/extconf.rb +144 -283
  17. data/ext/tiny_tds/extconsts.rb +4 -11
  18. data/ext/tiny_tds/result.c +68 -50
  19. data/ext/tiny_tds/tiny_tds_ext.c +4 -1
  20. data/lib/tiny_tds/bin.rb +44 -40
  21. data/lib/tiny_tds/client.rb +63 -55
  22. data/lib/tiny_tds/error.rb +0 -3
  23. data/lib/tiny_tds/gem.rb +23 -0
  24. data/lib/tiny_tds/result.rb +0 -3
  25. data/lib/tiny_tds.rb +37 -32
  26. data/{ports/patches/freetds/1.00 → patches/freetds/1.00.27}/0001-mingw_missing_inet_pton.diff +4 -4
  27. data/patches/freetds/1.00.27/0002-Don-t-use-MSYS2-file-libws2_32.diff +28 -0
  28. data/patches/libiconv/1.14/1-avoid-gets-error.patch +17 -0
  29. data/setup_cimgruby_dev.sh +25 -0
  30. data/start_dev.sh +21 -0
  31. data/tasks/native_gem.rake +16 -0
  32. data/tasks/package.rake +6 -0
  33. data/tasks/ports.rake +24 -0
  34. data/tasks/test.rake +7 -0
  35. data/test/bin/install-freetds.sh +18 -0
  36. data/test/bin/install-mssql.ps1 +42 -0
  37. data/test/bin/install-mssqltools.sh +9 -0
  38. data/test/bin/install-openssl.sh +18 -0
  39. data/test/bin/restore-from-native-gem.ps1 +10 -0
  40. data/test/bin/setup_tinytds_db.sh +7 -0
  41. data/test/bin/setup_volume_permissions.sh +10 -0
  42. data/test/client_test.rb +161 -112
  43. data/test/gem_test.rb +100 -0
  44. data/test/result_test.rb +293 -313
  45. data/test/schema_test.rb +369 -395
  46. data/test/sql/db-create.sql +18 -0
  47. data/test/sql/db-login.sql +38 -0
  48. data/test/test_helper.rb +116 -85
  49. data/test/thread_test.rb +22 -31
  50. data/tiny_tds.gemspec +27 -24
  51. metadata +109 -56
  52. data/appveyor.yml +0 -51
  53. data/test/appveyor/dbsetup.ps1 +0 -27
  54. data/test/appveyor/dbsetup.sql +0 -9
  55. data/test/benchmark/query.rb +0 -77
  56. data/test/benchmark/query_odbc.rb +0 -106
  57. data/test/benchmark/query_tinytds.rb +0 -126
  58. data/test/schema/sqlserver_2000.sql +0 -140
  59. data/test/schema/sqlserver_2005.sql +0 -140
  60. data/test/schema/sqlserver_2014.sql +0 -140
  61. data/test/schema/sybase_ase.sql +0 -138
  62. /data/bin/{defncopy → defncopy-ttds} +0 -0
  63. /data/bin/{tsql → tsql-ttds} +0 -0
  64. /data/test/schema/{sqlserver_2008.sql → sqlserver_2017.sql} +0 -0
@@ -0,0 +1,28 @@
1
+ From 56e8972f66c3e948e2ad6885595c58fd23dcdb37 Mon Sep 17 00:00:00 2001
2
+ From: Lars Kanis <kanis@comcard.de>
3
+ Date: Thu, 6 Jul 2017 17:09:40 +0200
4
+ Subject: [PATCH] Don't use MSYS2 file libws2_32.a for MINGW build
5
+
6
+ This file is intended for MSYS2/cygwin builds and blocks OpenSSL
7
+ detection of freetds on i686.
8
+ ---
9
+ configure | 2 --
10
+ configure.ac | 2 --
11
+ 2 files changed, 4 deletions(-)
12
+
13
+ diff --git a/configure b/configure
14
+ index 9495a49..31eb01d 100644
15
+ --- a/configure
16
+ +++ b/configure
17
+ @@ -15915,8 +15915,6 @@ case $host in
18
+ tds_mingw=yes
19
+ if test "$host_cpu" = "x86_64"; then
20
+ LIBS="-lws2_32"
21
+ - elif test -r /usr/lib/w32api/libws2_32.a; then
22
+ - LIBS="-L/usr/lib/w32api -lws2_32"
23
+ else
24
+ LIBS="-lws2_32"
25
+ fi
26
+ --
27
+ 2.6.2.windows.1
28
+
@@ -0,0 +1,17 @@
1
+ --- a/srclib/stdio.in.h 2017-01-22 01:23:00.000000000 -0400
2
+ +++ b/srclib/stdio.in.h 2017-01-22 01:24:00.000000000 -0400
3
+ @@ -695,8 +695,14 @@
4
+ /* It is very rare that the developer ever has full control of stdin,
5
+ so any use of gets warrants an unconditional warning. Assume it is
6
+ always declared, since it is required by C89. */
7
+ +#if defined(__GLIBC__) && !defined(__UCLIBC__)
8
+ +# ifdef __GLIBC_PREREQ
9
+ +# if !__GLIBC_PREREQ(2, 16)
10
+ _GL_WARN_ON_USE (gets, "gets is a security hole - use fgets instead");
11
+ +# endif
12
+ +# endif
13
+ +#endif
14
+ #endif
15
+
16
+
17
+ #if @GNULIB_OBSTACK_PRINTF@ || @GNULIB_OBSTACK_PRINTF_POSIX@
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -x
4
+ set -e
5
+
6
+ # this should mirror the steps outlined in the circleci yml
7
+ echo "Installing mssql-tools..."
8
+ sleep 5
9
+ sudo -E ./test/bin/install-mssqltools.sh
10
+
11
+ echo "Configurating tinytds test database..."
12
+ sleep 5
13
+ ./test/bin/setup_tinytds_db.sh
14
+
15
+ echo "Building openssl library..."
16
+ sleep 5
17
+ sudo -E ./test/bin/install-openssl.sh
18
+
19
+ echo "Building freetds library..."
20
+ sleep 5
21
+ sudo -E ./test/bin/install-freetds.sh
22
+
23
+ echo "Installing gems..."
24
+ sleep 5
25
+ bundle install
data/start_dev.sh ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -x
4
+ set -e
5
+
6
+ # set volume read/write permissions to work both outside and inside container
7
+ sudo ./test/bin/setup_volume_permissions.sh
8
+
9
+ docker-compose up -d
10
+ echo "Waiting for containers to start..."
11
+ sleep 10
12
+
13
+ # setup circleci ruby container for development
14
+ docker exec cimg_ruby bash -c './setup_cimgruby_dev.sh'
15
+
16
+ # enter container
17
+ set +x
18
+ echo "cimg/ruby container is ready for tiny_tds development.........."
19
+ echo "To enter container run: docker exec -it cimg_ruby /bin/bash"
20
+ echo "To build solution run: docker exec cimg_ruby bash -c 'bundle exec rake build'"
21
+ echo "To test solution run: docker exec cimg_ruby bash -c 'bundle exec rake test'"
@@ -0,0 +1,16 @@
1
+ CrossLibraries.each do |xlib|
2
+ platform = xlib.platform
3
+
4
+ desc "Build fat binary gem for platform #{platform}"
5
+ task "gem:native:#{platform}" do
6
+ require "rake_compiler_dock"
7
+
8
+ RakeCompilerDock.sh <<-EOT, platform: platform
9
+ bundle install &&
10
+ rake native:#{platform} pkg/#{SPEC.full_name}-#{platform}.gem MAKEOPTS=-j`nproc` RUBY_CC_VERSION=#{RakeCompilerDock.set_ruby_cc_version("~> 2.7", "~> 3.0")} MAKEFLAGS="V=1"
11
+ EOT
12
+ end
13
+
14
+ desc "Build the native binary gems"
15
+ multitask "gem:native" => "gem:native:#{platform}"
16
+ end
@@ -0,0 +1,6 @@
1
+ require "rubygems/package_task"
2
+
3
+ Gem::PackageTask.new(SPEC) do |pkg|
4
+ pkg.need_tar = false
5
+ pkg.need_zip = false
6
+ end
data/tasks/ports.rake ADDED
@@ -0,0 +1,24 @@
1
+ require_relative "../ext/tiny_tds/extconsts"
2
+
3
+ namespace :ports do
4
+ libraries_to_compile = {
5
+ openssl: OPENSSL_VERSION,
6
+ libiconv: ICONV_VERSION,
7
+ freetds: FREETDS_VERSION
8
+ }
9
+
10
+ desc "Notes the actual versions for the compiled ports into a file"
11
+ task "version_file", [:gem_platform] do |_task, args|
12
+ args.with_defaults(gem_platform: RbConfig::CONFIG["arch"])
13
+
14
+ ports_version = {}
15
+
16
+ libraries_to_compile.each do |library, version|
17
+ ports_version[library] = version
18
+ end
19
+
20
+ ports_version[:platform] = args.gem_platform
21
+
22
+ File.write(".ports_versions", ports_version)
23
+ end
24
+ end
data/tasks/test.rake ADDED
@@ -0,0 +1,7 @@
1
+ require "rake/testtask"
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << "test"
5
+ t.test_files = FileList["test/**/*_test.rb"]
6
+ t.verbose = true
7
+ end
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -x
4
+ set -e
5
+
6
+ if [ -z "$FREETDS_VERSION" ]; then
7
+ FREETDS_VERSION=$(ruby -r "./ext/tiny_tds/extconsts.rb" -e "puts FREETDS_VERSION")
8
+ fi
9
+
10
+ wget http://www.freetds.org/files/stable/freetds-$FREETDS_VERSION.tar.gz
11
+ tar -xzf freetds-$FREETDS_VERSION.tar.gz
12
+ cd freetds-$FREETDS_VERSION
13
+ ./configure
14
+ make
15
+ sudo make install
16
+ cd ..
17
+ rm -rf freetds-$FREETDS_VERSION
18
+ rm freetds-$FREETDS_VERSION.tar.gz
@@ -0,0 +1,42 @@
1
+ param ([int] $Version)
2
+
3
+ $ProgressPreference = 'SilentlyContinue'
4
+
5
+ $DownloadLinkTable = @{
6
+ 2017 = "https://go.microsoft.com/fwlink/?linkid=829176";
7
+ 2019 = "https://download.microsoft.com/download/7/c/1/7c14e92e-bdcb-4f89-b7cf-93543e7112d1/SQLEXPR_x64_ENU.exe";
8
+ 2022 = "https://download.microsoft.com/download/3/8/d/38de7036-2433-4207-8eae-06e247e17b25/SQLEXPR_x64_ENU.exe";
9
+ }
10
+
11
+ $MajorVersionTable = @{
12
+ 2017 = 14;
13
+ 2019 = 15;
14
+ 2022 = 16;
15
+ }
16
+
17
+ if (-not(Test-path "C:\Downloads")) {
18
+ mkdir "C:\Downloads"
19
+ }
20
+
21
+ $sqlInstallationFile = "C:\Downloads\sqlexpress.exe"
22
+ if (-not(Test-path $sqlInstallationFile -PathType leaf)) {
23
+ Write-Host "Downloading SQL Express ..."
24
+ Invoke-WebRequest -Uri $DownloadLinkTable[$Version] -OutFile "C:\Downloads\sqlexpress.exe"
25
+ }
26
+
27
+ Write-Host "Installing SQL Express ..."
28
+ Start-Process -Wait -FilePath "C:\Downloads\sqlexpress.exe" -ArgumentList /qs, /x:"C:\Downloads\setup"
29
+ C:\Downloads\setup\setup.exe /q /ACTION=Install /INSTANCENAME=SQLEXPRESS /FEATURES=SQLEngine /UPDATEENABLED=0 /SQLSVCACCOUNT='NT AUTHORITY\System' /SQLSYSADMINACCOUNTS='BUILTIN\ADMINISTRATORS' /TCPENABLED=1 /NPENABLED=0 /IACCEPTSQLSERVERLICENSETERMS
30
+
31
+ Write-Host "Configuring SQL Express ..."
32
+ stop-service MSSQL`$SQLEXPRESS
33
+ set-itemproperty -path "HKLM:\software\microsoft\microsoft sql server\mssql$($MajorVersionTable[$Version]).SQLEXPRESS\mssqlserver\supersocketnetlib\tcp\ipall" -name tcpdynamicports -value ''
34
+ set-itemproperty -path "HKLM:\software\microsoft\microsoft sql server\mssql$($MajorVersionTable[$Version]).SQLEXPRESS\mssqlserver\supersocketnetlib\tcp\ipall" -name tcpport -value 1433
35
+ set-itemproperty -path "HKLM:\software\microsoft\microsoft sql server\mssql$($MajorVersionTable[$Version]).SQLEXPRESS\mssqlserver\" -name LoginMode -value 2
36
+
37
+ Write-Host "Starting SQL Express ..."
38
+ start-service MSSQL`$SQLEXPRESS
39
+
40
+ Write-Host "Configuring MSSQL for TinyTDS ..."
41
+ & sqlcmd -i './test/sql/db-create.sql'
42
+ & sqlcmd -i './test/sql/db-login.sql'
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -x
4
+ set -e
5
+
6
+ curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
7
+ curl https://packages.microsoft.com/config/ubuntu/20.04/prod.list | sudo tee /etc/apt/sources.list.d/msprod.list
8
+ sudo apt-get update
9
+ sudo ACCEPT_EULA=Y apt-get -y install mssql-tools unixodbc-dev
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -x
4
+ set -e
5
+
6
+ if [ -z "$OPENSSL_VERSION" ]; then
7
+ OPENSSL_VERSION=$(ruby -r "./ext/tiny_tds/extconsts.rb" -e "puts OPENSSL_VERSION")
8
+ fi
9
+
10
+ wget https://www.openssl.org/source/openssl-$OPENSSL_VERSION.tar.gz
11
+ tar -xzf openssl-$OPENSSL_VERSION.tar.gz
12
+ cd openssl-$OPENSSL_VERSION
13
+ ./config --prefix=/opt/local --openssldir=/opt/local
14
+ make
15
+ make install_sw install_ssldirs
16
+ cd ..
17
+ rm -rf openssl-$OPENSSL_VERSION
18
+ rm openssl-$OPENSSL_VERSION.tar.gz
@@ -0,0 +1,10 @@
1
+ $gemVersion = (Get-Content VERSION).Trim()
2
+ $gemToUnpack = "./tiny_tds-$gemVersion-$env:RUBY_ARCHITECTURE.gem"
3
+
4
+ Write-Host "Looking to unpack $gemToUnpack"
5
+ gem unpack --target ./tmp "$gemToUnpack"
6
+
7
+ # Restore precompiled code
8
+ $source = (Resolve-Path ".\tmp\tiny_tds-$gemVersion-$env:RUBY_ARCHITECTURE\lib\tiny_tds").Path
9
+ $destination = (Resolve-Path ".\lib\tiny_tds").Path
10
+ Get-ChildItem $source -Recurse -Exclude "*.rb" | Copy-Item -Destination {Join-Path $destination $_.FullName.Substring($source.length)}
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -x
4
+ set -e
5
+
6
+ /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P $SA_PASSWORD -i ./test/sql/db-create.sql
7
+ /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P $SA_PASSWORD -i ./test/sql/db-login.sql
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -x
4
+
5
+ sudo groupadd -g 3434 circleci_tinytds
6
+ sudo usermod -a -G circleci_tinytds $USER
7
+ sudo useradd circleci_tinytds -u 3434 -g 3434
8
+ sudo usermod -a -G circleci_tinytds circleci_tinytds
9
+ sudo chgrp -R circleci_tinytds .
10
+ sudo chmod -R g+rwx .
data/test/client_test.rb CHANGED
@@ -1,113 +1,123 @@
1
- # encoding: utf-8
2
- require 'test_helper'
1
+ require "test_helper"
3
2
 
4
3
  class ClientTest < TinyTds::TestCase
5
-
6
- describe 'With valid credentials' do
7
-
4
+ describe "with valid credentials" do
8
5
  before do
9
6
  @client = new_connection
10
7
  end
11
8
 
12
- it 'must not be closed' do
9
+ it "must not be closed" do
13
10
  assert !@client.closed?
14
11
  assert @client.active?
15
12
  end
16
13
 
17
- it 'allows client connection to be closed' do
14
+ it "allows client connection to be closed" do
18
15
  assert @client.close
19
16
  assert @client.closed?
20
17
  assert !@client.active?
21
- action = lambda { @client.execute('SELECT 1 as [one]').each }
18
+ assert @client.dead?
19
+ action = lambda { @client.execute("SELECT 1 as [one]").each }
22
20
  assert_raise_tinytds_error(action) do |e|
23
- assert_match %r{closed connection}i, e.message, 'ignore if non-english test run'
21
+ assert_match %r{closed connection}i, e.message, "ignore if non-english test run"
24
22
  end
25
23
  end
26
24
 
27
- it 'has getters for the tds version information (brittle since conf takes precedence)' do
28
- if sybase_ase?
29
- assert_equal 7, @client.tds_version
30
- assert_equal 'DBTDS_5_0 - 5.0 SQL Server', @client.tds_version_info
31
- elsif @client.tds_73?
25
+ it "has getters for the tds version information (brittle since conf takes precedence)" do
26
+ if @client.tds_73?
32
27
  assert_equal 11, @client.tds_version
33
- assert_equal 'DBTDS_7_3 - Microsoft SQL Server 2008', @client.tds_version_info
28
+ assert_equal "DBTDS_7_3 - Microsoft SQL Server 2008", @client.tds_version_info
34
29
  else
35
30
  assert_equal 9, @client.tds_version
36
- assert_equal 'DBTDS_7_1/DBTDS_8_0 - Microsoft SQL Server 2000', @client.tds_version_info
31
+ assert_equal "DBTDS_7_1/DBTDS_8_0 - Microsoft SQL Server 2000", @client.tds_version_info
37
32
  end
38
33
  end
39
34
 
40
- it 'uses UTF-8 client charset/encoding by default' do
41
- assert_equal 'UTF-8', @client.charset
42
- assert_equal Encoding.find('UTF-8'), @client.encoding
35
+ it "uses UTF-8 client charset/encoding by default" do
36
+ assert_equal "UTF-8", @client.charset
37
+ assert_equal Encoding.find("UTF-8"), @client.encoding
43
38
  end
44
39
 
45
- it 'has a #escape method used for quote strings' do
40
+ it "has a #escape method used for quote strings" do
46
41
  assert_equal "''hello''", @client.escape("'hello'")
47
42
  end
48
43
 
49
- it 'allows valid iconv character set' do
50
- ['CP850', 'CP1252', 'ISO-8859-1'].each do |encoding|
51
- client = new_connection(:encoding => encoding)
44
+ ["CP850", "CP1252", "ISO-8859-1"].each do |encoding|
45
+ it "allows valid iconv character set - #{encoding}" do
46
+ client = new_connection(encoding: encoding)
52
47
  assert_equal encoding, client.charset
53
48
  assert_equal Encoding.find(encoding), client.encoding
54
- client.close
49
+ ensure
50
+ client&.close
55
51
  end
56
52
  end
57
53
 
58
- it 'must be able to use :host/:port connection' do
59
- host = ENV['TINYTDS_UNIT_HOST_TEST'] || ENV['TINYTDS_UNIT_HOST']
60
- port = ENV['TINYTDS_UNIT_PORT_TEST'] || ENV['TINYTDS_UNIT_PORT'] || 1433
61
- client = new_connection dataserver: nil, host: host, port: port
62
- client.close
63
- end unless sqlserver_azure?
64
-
54
+ unless sqlserver_azure?
55
+ it "must be able to use :host/:port connection" do
56
+ host = ENV["TINYTDS_UNIT_HOST_TEST"] || ENV["TINYTDS_UNIT_HOST"] || "localhost"
57
+ port = ENV["TINYTDS_UNIT_PORT_TEST"] || ENV["TINYTDS_UNIT_PORT"] || 1433
58
+ begin
59
+ client = new_connection dataserver: nil, host: host, port: port
60
+ ensure
61
+ client&.close
62
+ end
63
+ end
64
+ end
65
65
  end
66
66
 
67
- describe 'With in-valid options' do
67
+ describe "With in-valid options" do
68
+ before(:all) do
69
+ init_toxiproxy
70
+ end
68
71
 
69
- it 'raises an argument error when no :host given and :dataserver is blank' do
70
- assert_raises(ArgumentError) { new_connection :dataserver => nil, :host => nil }
72
+ it "raises an argument error when no :host given and :dataserver is blank" do
73
+ assert_raises(ArgumentError) { new_connection dataserver: nil, host: nil }
71
74
  end
72
75
 
73
- it 'raises an argument error when no :username is supplied' do
74
- assert_raises(ArgumentError) { TinyTds::Client.new :username => nil }
76
+ it "raises an argument error when no :username is supplied" do
77
+ assert_raises(ArgumentError) { TinyTds::Client.new username: nil }
75
78
  end
76
79
 
77
- it 'raises TinyTds exception with undefined :dataserver' do
78
- options = connection_options :login_timeout => 1, :dataserver => 'DOESNOTEXIST'
80
+ it "raises TinyTds exception with undefined :dataserver" do
81
+ options = connection_options login_timeout: 1, dataserver: "DOESNOTEXIST"
79
82
  action = lambda { new_connection(options) }
80
83
  assert_raise_tinytds_error(action) do |e|
81
- assert_equal 20012, e.db_error_number
82
- assert_equal 2, e.severity
83
- assert_match %r{server name not found in configuration files}i, e.message, 'ignore if non-english test run'
84
+ # Not sure why tese are different.
85
+ if ruby_darwin?
86
+ assert_equal 20009, e.db_error_number
87
+ assert_equal 9, e.severity
88
+ assert_match %r{is unavailable or does not exist}i, e.message, "ignore if non-english test run"
89
+ else
90
+ assert_equal 20012, e.db_error_number
91
+ assert_equal 2, e.severity
92
+ assert_match %r{server name not found in configuration files}i, e.message, "ignore if non-english test run"
93
+ end
84
94
  end
85
95
  assert_new_connections_work
86
96
  end
87
97
 
88
- it 'raises TinyTds exception with long query past :timeout option' do
89
- client = new_connection :timeout => 1
98
+ it "raises TinyTds exception with long query past :timeout option" do
99
+ client = new_connection timeout: 1
90
100
  action = lambda { client.execute("WaitFor Delay '00:00:02'").do }
91
101
  assert_raise_tinytds_error(action) do |e|
92
102
  assert_equal 20003, e.db_error_number
93
103
  assert_equal 6, e.severity
94
- assert_match %r{timed out}i, e.message, 'ignore if non-english test run'
104
+ assert_match %r{timed out}i, e.message, "ignore if non-english test run"
95
105
  end
96
106
  assert_client_works(client)
97
107
  close_client(client)
98
108
  assert_new_connections_work
99
109
  end
100
110
 
101
- it 'must not timeout per sql batch when not under transaction' do
102
- client = new_connection :timeout => 2
111
+ it "must not timeout per sql batch when not under transaction" do
112
+ client = new_connection timeout: 2
103
113
  client.execute("WaitFor Delay '00:00:01'").do
104
114
  client.execute("WaitFor Delay '00:00:01'").do
105
115
  client.execute("WaitFor Delay '00:00:01'").do
106
116
  close_client(client)
107
117
  end
108
118
 
109
- it 'must not timeout per sql batch when under transaction' do
110
- client = new_connection :timeout => 2
119
+ it "must not timeout per sql batch when under transaction" do
120
+ client = new_connection timeout: 2
111
121
  begin
112
122
  client.execute("BEGIN TRANSACTION").do
113
123
  client.execute("WaitFor Delay '00:00:01'").do
@@ -119,99 +129,138 @@ class ClientTest < TinyTds::TestCase
119
129
  end
120
130
  end
121
131
 
122
- it 'must run this test to prove we account for dropped connections' do
123
- skip
124
- begin
125
- client = new_connection :login_timeout => 2, :timeout => 2
126
- assert_client_works(client)
127
- STDOUT.puts "Disconnect network!"
128
- sleep 10
129
- STDOUT.puts "This should not get stuck past 6 seconds!"
130
- action = lambda { client.execute('SELECT 1 as [one]').each }
132
+ it "raises TinyTds exception with tcp socket network failure" do
133
+ client = new_connection timeout: 2, port: 1234, host: ENV["TOXIPROXY_HOST"]
134
+ assert_client_works(client)
135
+ action = lambda { client.execute("waitfor delay '00:00:05'").do }
136
+
137
+ # Use toxiproxy to close the TCP socket after 1 second.
138
+ # We want TinyTds to execute the statement, hit the timeout configured above, and then not be able to use the network to cancel
139
+ # the network connection needs to close after the sql batch is sent and before the timeout above is hit
140
+ Toxiproxy[:sqlserver_test].toxic(:slow_close, delay: 1000).apply do
131
141
  assert_raise_tinytds_error(action) do |e|
132
142
  assert_equal 20003, e.db_error_number
133
143
  assert_equal 6, e.severity
134
- assert_match %r{timed out}i, e.message, 'ignore if non-english test run'
144
+ assert_match %r{timed out}i, e.message, "ignore if non-english test run"
135
145
  end
136
- ensure
137
- STDOUT.puts "Reconnect network!"
138
- sleep 10
139
- action = lambda { client.execute('SELECT 1 as [one]').each }
140
- assert_raise_tinytds_error(action) do |e|
141
- assert_equal 20047, e.db_error_number
142
- assert_equal 1, e.severity
143
- assert_match %r{dead or not enabled}i, e.message, 'ignore if non-english test run'
146
+ end
147
+ ensure
148
+ assert_new_connections_work
149
+ end
150
+
151
+ it "raises TinyTds exception with dead connection network failure" do
152
+ skip if ruby_windows?
153
+
154
+ begin
155
+ client = new_connection timeout: 2, port: 1234, host: ENV["TOXIPROXY_HOST"]
156
+ assert_client_works(client)
157
+ action = lambda { client.execute("waitfor delay '00:00:05'").do }
158
+
159
+ # Use toxiproxy to close the network connection after 1 second.
160
+ # We want TinyTds to execute the statement, hit the timeout configured above, and then not be able to use the network to cancel
161
+ # the network connection needs to close after the sql batch is sent and before the timeout above is hit
162
+ Toxiproxy[:sqlserver_test].toxic(:timeout, timeout: 1000).apply do
163
+ assert_raise_tinytds_error(action) do |e|
164
+ assert_equal 20047, e.db_error_number
165
+ assert_includes [1, 9], e.severity
166
+ assert_match %r{dead or not enabled}i, e.message, "ignore if non-english test run"
167
+ end
144
168
  end
145
- close_client(client)
169
+ ensure
146
170
  assert_new_connections_work
147
171
  end
148
172
  end
149
173
 
150
- it 'raises TinyTds exception with wrong :username' do
151
- skip if ENV['CI'] && sqlserver_azure? # Some issue with db_error_number.
152
- options = connection_options :username => 'willnotwork'
174
+ it "raises TinyTds exception with login timeout" do
175
+ action = lambda do
176
+ Toxiproxy[:sqlserver_test].toxic(:timeout, timeout: 0).apply do
177
+ new_connection login_timeout: 1, port: 1234, host: ENV["TOXIPROXY_HOST"]
178
+ end
179
+ end
180
+ assert_raise_tinytds_error(action) do |e|
181
+ assert_equal 20003, e.db_error_number
182
+ assert_equal 6, e.severity
183
+ assert_match %r{timed out}i, e.message, "ignore if non-english test run"
184
+ end
185
+ ensure
186
+ assert_new_connections_work
187
+ end
188
+
189
+ it "raises TinyTds exception with wrong :username" do
190
+ skip if ENV["CI"] && sqlserver_azure? # Some issue with db_error_number.
191
+ options = connection_options username: "willnotwork"
153
192
  action = lambda { new_connection(options) }
154
193
  assert_raise_tinytds_error(action) do |e|
155
- assert_equal sybase_ase? ? 4002 : 18456, e.db_error_number
194
+ assert_equal 18456, e.db_error_number
156
195
  assert_equal 14, e.severity
157
- assert_match %r{login failed}i, e.message, 'ignore if non-english test run'
196
+ assert_match %r{login failed}i, e.message, "ignore if non-english test run"
158
197
  end
159
198
  assert_new_connections_work
160
199
  end
161
-
162
200
  end
163
201
 
164
- describe 'Private methods' do
165
-
202
+ describe "#parse_username" do
166
203
  let(:client) { @client = new_connection }
167
204
 
168
- it '#parse_username returns username if azure is not true' do
169
- username = 'user@abc123.database.windows.net'
170
- client.send(:parse_username, username: username).must_equal username
205
+ it "returns username if azure is not true" do
206
+ _(
207
+ client.send(:parse_username, username: "user@abc123.database.windows.net")
208
+ ).must_equal "user@abc123.database.windows.net"
171
209
  end
172
210
 
173
- it '#parse_username returns short username if azure is true' do
174
- client.send(:parse_username,
175
- username: 'user@abc123.database.windows.net',
176
- host: 'abc123.database.windows.net',
177
- azure: true
178
- ).must_equal 'user@abc123'
211
+ it "returns short username if azure is true" do
212
+ _(
213
+ client.send(
214
+ :parse_username,
215
+ username: "user@abc123.database.windows.net",
216
+ host: "abc123.database.windows.net",
217
+ azure: true
218
+ )
219
+ ).must_equal "user@abc123"
179
220
  end
180
221
 
181
- it '#parse_username returns full username if azure is false' do
182
- client.send(:parse_username,
183
- username: 'user@abc123.database.windows.net',
184
- host: 'abc123.database.windows.net',
185
- azure: false
186
- ).must_equal 'user@abc123.database.windows.net'
222
+ it "returns full username if azure is false" do
223
+ _(
224
+ client.send(
225
+ :parse_username,
226
+ username: "user@abc123.database.windows.net",
227
+ host: "abc123.database.windows.net",
228
+ azure: false
229
+ )
230
+ ).must_equal "user@abc123.database.windows.net"
187
231
  end
188
232
 
189
- it '#parse_username returns short username if passed and azure is true' do
190
- client.send(:parse_username,
191
- username: 'user@abc123',
192
- host: 'abc123.database.windows.net',
193
- azure: true
194
- ).must_equal 'user@abc123'
233
+ it "returns short username if passed and azure is true" do
234
+ _(
235
+ client.send(
236
+ :parse_username,
237
+ username: "user@abc123",
238
+ host: "abc123.database.windows.net",
239
+ azure: true
240
+ )
241
+ ).must_equal "user@abc123"
195
242
  end
196
243
 
197
- it '#parse_username returns username with servername if passed and azure is true' do
198
- client.send(:parse_username,
199
- username: 'user',
200
- host: 'abc123.database.windows.net',
201
- azure: true
202
- ).must_equal 'user@abc123'
244
+ it "returns username with servername if passed and azure is true" do
245
+ _(
246
+ client.send(
247
+ :parse_username,
248
+ username: "user",
249
+ host: "abc123.database.windows.net",
250
+ azure: true
251
+ )
252
+ ).must_equal "user@abc123"
203
253
  end
204
254
 
205
- it '#parse_username returns username with servername if passed and azure is false' do
206
- client.send(:parse_username,
207
- username: 'user',
208
- host: 'abc123.database.windows.net',
209
- azure: false
210
- ).must_equal 'user'
255
+ it "returns username with servername if passed and azure is false" do
256
+ _(
257
+ client.send(
258
+ :parse_username,
259
+ username: "user",
260
+ host: "abc123.database.windows.net",
261
+ azure: false
262
+ )
263
+ ).must_equal "user"
211
264
  end
212
-
213
265
  end
214
-
215
-
216
266
  end
217
-