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
@@ -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
@@ -2,9 +2,7 @@
2
2
  require 'test_helper'
3
3
 
4
4
  class ClientTest < TinyTds::TestCase
5
-
6
- describe 'With valid credentials' do
7
-
5
+ describe 'with valid credentials' do
8
6
  before do
9
7
  @client = new_connection
10
8
  end
@@ -18,6 +16,7 @@ class ClientTest < TinyTds::TestCase
18
16
  assert @client.close
19
17
  assert @client.closed?
20
18
  assert !@client.active?
19
+ assert @client.dead?
21
20
  action = lambda { @client.execute('SELECT 1 as [one]').each }
22
21
  assert_raise_tinytds_error(action) do |e|
23
22
  assert_match %r{closed connection}i, e.message, 'ignore if non-english test run'
@@ -25,10 +24,7 @@ class ClientTest < TinyTds::TestCase
25
24
  end
26
25
 
27
26
  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?
27
+ if @client.tds_73?
32
28
  assert_equal 11, @client.tds_version
33
29
  assert_equal 'DBTDS_7_3 - Microsoft SQL Server 2008', @client.tds_version_info
34
30
  else
@@ -67,10 +63,12 @@ class ClientTest < TinyTds::TestCase
67
63
  client.close if client
68
64
  end
69
65
  end unless sqlserver_azure?
70
-
71
66
  end
72
67
 
73
68
  describe 'With in-valid options' do
69
+ before(:all) do
70
+ init_toxiproxy
71
+ end
74
72
 
75
73
  it 'raises an argument error when no :host given and :dataserver is blank' do
76
74
  assert_raises(ArgumentError) { new_connection :dataserver => nil, :host => nil }
@@ -132,30 +130,63 @@ class ClientTest < TinyTds::TestCase
132
130
  end
133
131
  end
134
132
 
135
- it 'must run this test to prove we account for dropped connections' do
136
- skip
133
+ it 'raises TinyTds exception with tcp socket network failure' do
137
134
  begin
138
- client = new_connection :login_timeout => 2, :timeout => 2
135
+ client = new_connection timeout: 2, port: 1234, host: ENV['TOXIPROXY_HOST']
139
136
  assert_client_works(client)
140
- STDOUT.puts "Disconnect network!"
141
- sleep 10
142
- STDOUT.puts "This should not get stuck past 6 seconds!"
143
- action = lambda { client.execute('SELECT 1 as [one]').each }
137
+ action = lambda { client.execute("waitfor delay '00:00:05'").do }
138
+
139
+ # Use toxiproxy to close the TCP socket after 1 second.
140
+ # We want TinyTds to execute the statement, hit the timeout configured above, and then not be able to use the network to cancel
141
+ # the network connection needs to close after the sql batch is sent and before the timeout above is hit
142
+ Toxiproxy[:sqlserver_test].toxic(:slow_close, delay: 1000).apply do
143
+ assert_raise_tinytds_error(action) do |e|
144
+ assert_equal 20003, e.db_error_number
145
+ assert_equal 6, e.severity
146
+ assert_match %r{timed out}i, e.message, 'ignore if non-english test run'
147
+ end
148
+ end
149
+ ensure
150
+ assert_new_connections_work
151
+ end
152
+ end
153
+
154
+ it 'raises TinyTds exception with dead connection network failure' do
155
+ skip if ruby_windows?
156
+
157
+ begin
158
+ client = new_connection timeout: 2, port: 1234, host: ENV['TOXIPROXY_HOST']
159
+ assert_client_works(client)
160
+ action = lambda { client.execute("waitfor delay '00:00:05'").do }
161
+
162
+ # Use toxiproxy to close the network connection after 1 second.
163
+ # We want TinyTds to execute the statement, hit the timeout configured above, and then not be able to use the network to cancel
164
+ # the network connection needs to close after the sql batch is sent and before the timeout above is hit
165
+ Toxiproxy[:sqlserver_test].toxic(:timeout, timeout: 1000).apply do
166
+ assert_raise_tinytds_error(action) do |e|
167
+ assert_equal 20047, e.db_error_number
168
+ assert_includes [1,9], e.severity
169
+ assert_match %r{dead or not enabled}i, e.message, 'ignore if non-english test run'
170
+ end
171
+ end
172
+ ensure
173
+ assert_new_connections_work
174
+ end
175
+ end
176
+
177
+ it 'raises TinyTds exception with login timeout' do
178
+ begin
179
+ action = lambda do
180
+ Toxiproxy[:sqlserver_test].toxic(:timeout, timeout: 0).apply do
181
+ new_connection login_timeout: 1, port: 1234, host: ENV['TOXIPROXY_HOST']
182
+ end
183
+ end
144
184
  assert_raise_tinytds_error(action) do |e|
145
185
  assert_equal 20003, e.db_error_number
146
186
  assert_equal 6, e.severity
147
187
  assert_match %r{timed out}i, e.message, 'ignore if non-english test run'
148
188
  end
149
189
  ensure
150
- STDOUT.puts "Reconnect network!"
151
- sleep 10
152
- action = lambda { client.execute('SELECT 1 as [one]').each }
153
- assert_raise_tinytds_error(action) do |e|
154
- assert_equal 20047, e.db_error_number
155
- assert_equal 1, e.severity
156
- assert_match %r{dead or not enabled}i, e.message, 'ignore if non-english test run'
157
- end
158
- close_client(client)
159
190
  assert_new_connections_work
160
191
  end
161
192
  end
@@ -165,7 +196,7 @@ class ClientTest < TinyTds::TestCase
165
196
  options = connection_options :username => 'willnotwork'
166
197
  action = lambda { new_connection(options) }
167
198
  assert_raise_tinytds_error(action) do |e|
168
- assert_equal sybase_ase? ? 4002 : 18456, e.db_error_number
199
+ assert_equal 18456, e.db_error_number
169
200
  assert_equal 14, e.severity
170
201
  assert_match %r{login failed}i, e.message, 'ignore if non-english test run'
171
202
  end
@@ -174,57 +205,68 @@ class ClientTest < TinyTds::TestCase
174
205
 
175
206
  end
176
207
 
177
- describe 'Private methods' do
178
-
208
+ describe '#parse_username' do
179
209
  let(:client) { @client = new_connection }
180
210
 
181
- it '#parse_username returns username if azure is not true' do
182
- username = 'user@abc123.database.windows.net'
183
- client.send(:parse_username, username: username).must_equal username
211
+ it 'returns username if azure is not true' do
212
+ _(
213
+ client.send(:parse_username, username: 'user@abc123.database.windows.net')
214
+ ).must_equal 'user@abc123.database.windows.net'
184
215
  end
185
216
 
186
- it '#parse_username returns short username if azure is true' do
187
- client.send(:parse_username,
188
- username: 'user@abc123.database.windows.net',
189
- host: 'abc123.database.windows.net',
190
- azure: true
217
+ it 'returns short username if azure is true' do
218
+ _(
219
+ client.send(
220
+ :parse_username,
221
+ username: 'user@abc123.database.windows.net',
222
+ host: 'abc123.database.windows.net',
223
+ azure: true
224
+ )
191
225
  ).must_equal 'user@abc123'
192
226
  end
193
227
 
194
- it '#parse_username returns full username if azure is false' do
195
- client.send(:parse_username,
196
- username: 'user@abc123.database.windows.net',
197
- host: 'abc123.database.windows.net',
198
- azure: false
228
+ it 'returns full username if azure is false' do
229
+ _(
230
+ client.send(
231
+ :parse_username,
232
+ username: 'user@abc123.database.windows.net',
233
+ host: 'abc123.database.windows.net',
234
+ azure: false
235
+ )
199
236
  ).must_equal 'user@abc123.database.windows.net'
200
237
  end
201
238
 
202
- it '#parse_username returns short username if passed and azure is true' do
203
- client.send(:parse_username,
204
- username: 'user@abc123',
205
- host: 'abc123.database.windows.net',
206
- azure: true
239
+ it 'returns short username if passed and azure is true' do
240
+ _(
241
+ client.send(
242
+ :parse_username,
243
+ username: 'user@abc123',
244
+ host: 'abc123.database.windows.net',
245
+ azure: true
246
+ )
207
247
  ).must_equal 'user@abc123'
208
248
  end
209
249
 
210
- it '#parse_username returns username with servername if passed and azure is true' do
211
- client.send(:parse_username,
212
- username: 'user',
213
- host: 'abc123.database.windows.net',
214
- azure: true
250
+ it 'returns username with servername if passed and azure is true' do
251
+ _(
252
+ client.send(
253
+ :parse_username,
254
+ username: 'user',
255
+ host: 'abc123.database.windows.net',
256
+ azure: true
257
+ )
215
258
  ).must_equal 'user@abc123'
216
259
  end
217
260
 
218
- it '#parse_username returns username with servername if passed and azure is false' do
219
- client.send(:parse_username,
220
- username: 'user',
221
- host: 'abc123.database.windows.net',
222
- azure: false
261
+ it 'returns username with servername if passed and azure is false' do
262
+ _(
263
+ client.send(
264
+ :parse_username,
265
+ username: 'user',
266
+ host: 'abc123.database.windows.net',
267
+ azure: false
268
+ )
223
269
  ).must_equal 'user'
224
270
  end
225
-
226
271
  end
227
-
228
-
229
272
  end
230
-
data/test/gem_test.rb CHANGED
@@ -9,11 +9,11 @@ class GemTest < MiniTest::Spec
9
9
 
10
10
  # We're going to muck with some system globals so lets make sure
11
11
  # they get set back later
12
- original_host = RbConfig::CONFIG['host']
12
+ original_platform = RbConfig::CONFIG['arch']
13
13
  original_pwd = Dir.pwd
14
14
 
15
15
  after do
16
- RbConfig::CONFIG['host'] = original_host
16
+ RbConfig::CONFIG['arch'] = original_platform
17
17
  Dir.chdir original_pwd
18
18
  end
19
19
 
@@ -21,13 +21,13 @@ class GemTest < MiniTest::Spec
21
21
  let(:root_path) { TinyTds::Gem.root_path }
22
22
 
23
23
  it 'should be the root path' do
24
- root_path.must_equal gem_root
24
+ _(root_path).must_equal gem_root
25
25
  end
26
26
 
27
27
  it 'should be the root path no matter the cwd' do
28
28
  Dir.chdir '/'
29
29
 
30
- root_path.must_equal gem_root
30
+ _(root_path).must_equal gem_root
31
31
  end
32
32
  end
33
33
 
@@ -35,19 +35,19 @@ class GemTest < MiniTest::Spec
35
35
  let(:ports_root_path) { TinyTds::Gem.ports_root_path }
36
36
 
37
37
  it 'should be the ports path' do
38
- ports_root_path.must_equal File.join(gem_root,'ports')
38
+ _(ports_root_path).must_equal File.join(gem_root,'ports')
39
39
  end
40
40
 
41
41
  it 'should be the ports path no matter the cwd' do
42
42
  Dir.chdir '/'
43
43
 
44
- ports_root_path.must_equal File.join(gem_root,'ports')
44
+ _(ports_root_path).must_equal File.join(gem_root,'ports')
45
45
  end
46
46
  end
47
47
 
48
48
  describe '#ports_bin_paths' do
49
49
  let(:ports_bin_paths) { TinyTds::Gem.ports_bin_paths }
50
-
50
+
51
51
  describe 'when the ports directories exist' do
52
52
  let(:fake_bin_paths) do
53
53
  ports_host_root = File.join(gem_root, 'ports', 'fake-host-with-dirs')
@@ -61,7 +61,7 @@ class GemTest < MiniTest::Spec
61
61
  end
62
62
 
63
63
  before do
64
- RbConfig::CONFIG['host'] = 'fake-host-with-dirs'
64
+ RbConfig::CONFIG['arch'] = 'fake-host-with-dirs'
65
65
  fake_bin_paths.each do |path|
66
66
  FileUtils.mkdir_p(path)
67
67
  end
@@ -74,34 +74,34 @@ class GemTest < MiniTest::Spec
74
74
  end
75
75
 
76
76
  it 'should return all the bin directories' do
77
- ports_bin_paths.sort.must_equal fake_bin_paths.sort
77
+ _(ports_bin_paths.sort).must_equal fake_bin_paths.sort
78
78
  end
79
79
 
80
80
  it 'should return all the bin directories regardless of cwd' do
81
81
  Dir.chdir '/'
82
- ports_bin_paths.sort.must_equal fake_bin_paths.sort
82
+ _(ports_bin_paths.sort).must_equal fake_bin_paths.sort
83
83
  end
84
84
  end
85
85
 
86
86
  describe 'when the ports directories are missing' do
87
87
  before do
88
- RbConfig::CONFIG['host'] = 'fake-host-without-dirs'
88
+ RbConfig::CONFIG['arch'] = 'fake-host-without-dirs'
89
89
  end
90
90
 
91
91
  it 'should return no directories' do
92
- ports_bin_paths.must_be_empty
92
+ _(ports_bin_paths).must_be_empty
93
93
  end
94
94
 
95
95
  it 'should return no directories regardless of cwd' do
96
96
  Dir.chdir '/'
97
- ports_bin_paths.must_be_empty
97
+ _(ports_bin_paths).must_be_empty
98
98
  end
99
99
  end
100
100
  end
101
101
 
102
102
  describe '#ports_lib_paths' do
103
103
  let(:ports_lib_paths) { TinyTds::Gem.ports_lib_paths }
104
-
104
+
105
105
  describe 'when the ports directories exist' do
106
106
  let(:fake_lib_paths) do
107
107
  ports_host_root = File.join(gem_root, 'ports', 'fake-host-with-dirs')
@@ -115,7 +115,7 @@ class GemTest < MiniTest::Spec
115
115
  end
116
116
 
117
117
  before do
118
- RbConfig::CONFIG['host'] = 'fake-host-with-dirs'
118
+ RbConfig::CONFIG['arch'] = 'fake-host-with-dirs'
119
119
  fake_lib_paths.each do |path|
120
120
  FileUtils.mkdir_p(path)
121
121
  end
@@ -128,48 +128,45 @@ class GemTest < MiniTest::Spec
128
128
  end
129
129
 
130
130
  it 'should return all the lib directories' do
131
- ports_lib_paths.sort.must_equal fake_lib_paths.sort
131
+ _(ports_lib_paths.sort).must_equal fake_lib_paths.sort
132
132
  end
133
133
 
134
134
  it 'should return all the lib directories regardless of cwd' do
135
135
  Dir.chdir '/'
136
- ports_lib_paths.sort.must_equal fake_lib_paths.sort
136
+ _(ports_lib_paths.sort).must_equal fake_lib_paths.sort
137
137
  end
138
138
  end
139
139
 
140
140
  describe 'when the ports directories are missing' do
141
141
  before do
142
- RbConfig::CONFIG['host'] = 'fake-host-without-dirs'
142
+ RbConfig::CONFIG['arch'] = 'fake-host-without-dirs'
143
143
  end
144
144
 
145
145
 
146
146
  it 'should return no directories' do
147
- ports_lib_paths.must_be_empty
147
+ _(ports_lib_paths).must_be_empty
148
148
  end
149
149
 
150
150
  it 'should return no directories regardless of cwd' do
151
151
  Dir.chdir '/'
152
- ports_lib_paths.must_be_empty
152
+ _(ports_lib_paths).must_be_empty
153
153
  end
154
154
  end
155
155
  end
156
156
 
157
157
  describe '#ports_host' do
158
158
  {
159
- 'i686-pc-linux-gnu' => 'i686-pc-linux-gnu',
160
- 'x86_64-pc-linux-gnu' => 'x86_64-pc-linux-gnu',
161
- 'i686-w64-mingw32' => 'i686-w64-mingw32',
162
- 'x86_64-w64-mingw32' => 'x86_64-w64-mingw32',
163
- # consolidate this host to our build w64-mingw32 arch
164
- 'i686-pc-mingw32' => 'i686-w64-mingw32'
159
+ 'x64-mingw-ucrt' => 'x64-mingw-ucrt',
160
+ 'x64-mingw32' => 'x64-mingw32',
161
+ 'x86_64-linux' => 'x86_64-linux',
165
162
  }.each do |host,expected|
166
163
  describe "on a #{host} architecture" do
167
164
  before do
168
- RbConfig::CONFIG['host'] = host
165
+ RbConfig::CONFIG['arch'] = host
169
166
  end
170
167
 
171
168
  it "should return a #{expected} ports host" do
172
- TinyTds::Gem.ports_host.must_equal expected
169
+ _(TinyTds::Gem.ports_host).must_equal expected
173
170
  end
174
171
  end
175
172
  end