tiny_tds 2.1.2-x86-mingw32 → 2.1.4-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,9 +5,9 @@
5
5
  void init_tinytds_client();
6
6
 
7
7
  #define ERROR_MSG_SIZE 1024
8
+ #define ERRORS_STACK_INIT_SIZE 2
8
9
 
9
10
  typedef struct {
10
- short int is_set;
11
11
  int is_message;
12
12
  int cancel;
13
13
  char error[ERROR_MSG_SIZE];
@@ -25,7 +25,9 @@ typedef struct {
25
25
  RETCODE dbsqlok_retcode;
26
26
  short int dbcancel_sent;
27
27
  short int nonblocking;
28
- tinytds_errordata nonblocking_error;
28
+ short int nonblocking_errors_length;
29
+ short int nonblocking_errors_size;
30
+ tinytds_errordata *nonblocking_errors;
29
31
  VALUE message_handler;
30
32
  } tinytds_client_userdata;
31
33
 
@@ -40,7 +42,7 @@ typedef struct {
40
42
  rb_encoding *encoding;
41
43
  } tinytds_client_wrapper;
42
44
 
43
- VALUE rb_tinytds_raise_error(DBPROCESS *dbproc, int is_message, int cancel, const char *error, const char *source, int severity, int dberr, int oserr);
45
+ VALUE rb_tinytds_raise_error(DBPROCESS *dbproc, tinytds_errordata error);
44
46
 
45
47
  // Lib Macros
46
48
 
@@ -22,35 +22,39 @@ do_help if arg_config('--help')
22
22
 
23
23
  # Make sure to check the ports path for the configured host
24
24
  host = RbConfig::CONFIG['host']
25
- project_dir = File.join(['..']*4)
25
+ project_dir = File.expand_path("../../..", __FILE__)
26
26
  freetds_ports_dir = File.join(project_dir, 'ports', host, 'freetds', FREETDS_VERSION)
27
27
  freetds_ports_dir = File.expand_path(freetds_ports_dir)
28
28
 
29
29
  # Add all the special path searching from the original tiny_tds build
30
- # order is important here! First in, last searched.
30
+ # order is important here! First in, first searched.
31
31
  DIRS = %w(
32
- /usr/local
33
32
  /opt/local
33
+ /usr/local
34
34
  )
35
35
 
36
+ # Add the ports directory if it exists for local developer builds
37
+ DIRS.unshift(freetds_ports_dir) if File.directory?(freetds_ports_dir)
38
+
36
39
  # Grab freetds environment variable for use by people on services like
37
40
  # Heroku who they can't easily use bundler config to set directories
38
- DIRS.push(ENV['FREETDS_DIR']) if ENV.has_key?('FREETDS_DIR')
39
-
40
- # Add the ports directory if it exists for local developer builds
41
- DIRS.push(freetds_ports_dir) if File.directory?(freetds_ports_dir)
41
+ DIRS.unshift(ENV['FREETDS_DIR']) if ENV.has_key?('FREETDS_DIR')
42
42
 
43
43
  # Add the search paths for freetds configured above
44
- DIRS.each do |path|
45
- idir = "#{path}/include"
44
+ ldirs = DIRS.flat_map do |path|
46
45
  ldir = "#{path}/lib"
46
+ [ldir, "#{ldir}/freetds"]
47
+ end
47
48
 
48
- dir_config('freetds',
49
- [idir, "#{idir}/freetds"],
50
- [ldir, "#{ldir}/freetds"]
51
- )
49
+ idirs = DIRS.flat_map do |path|
50
+ idir = "#{path}/include"
51
+ [idir, "#{idir}/freetds"]
52
52
  end
53
53
 
54
+ puts "looking for freetds headers in the following directories:\n#{idirs.map{|a| " - #{a}\n"}.join}"
55
+ puts "looking for freetds library in the following directories:\n#{ldirs.map{|a| " - #{a}\n"}.join}"
56
+ dir_config('freetds', idirs, ldirs)
57
+
54
58
  have_dependencies = [
55
59
  find_header('sybfront.h'),
56
60
  find_header('sybdb.h'),
@@ -2,10 +2,10 @@
2
2
  ICONV_VERSION = ENV['TINYTDS_ICONV_VERSION'] || "1.15"
3
3
  ICONV_SOURCE_URI = "http://ftp.gnu.org/pub/gnu/libiconv/libiconv-#{ICONV_VERSION}.tar.gz"
4
4
 
5
- OPENSSL_VERSION = ENV['TINYTDS_OPENSSL_VERSION'] || '1.1.0e'
5
+ OPENSSL_VERSION = ENV['TINYTDS_OPENSSL_VERSION'] || '1.1.1d'
6
6
  OPENSSL_SOURCE_URI = "https://www.openssl.org/source/openssl-#{OPENSSL_VERSION}.tar.gz"
7
7
 
8
- FREETDS_VERSION = ENV['TINYTDS_FREETDS_VERSION'] || "1.00.27"
8
+ FREETDS_VERSION = ENV['TINYTDS_FREETDS_VERSION'] || "1.1.24"
9
9
  FREETDS_VERSION_INFO = Hash.new { |h,k|
10
10
  h[k] = {files: "http://www.freetds.org/files/stable/freetds-#{k}.tar.bz2"}
11
11
  }
@@ -86,26 +86,38 @@ static void dbcancel_ubf(DBPROCESS *client) {
86
86
  static void nogvl_setup(DBPROCESS *client) {
87
87
  GET_CLIENT_USERDATA(client);
88
88
  userdata->nonblocking = 1;
89
+ userdata->nonblocking_errors_length = 0;
90
+ userdata->nonblocking_errors = malloc(ERRORS_STACK_INIT_SIZE * sizeof(tinytds_errordata));
91
+ userdata->nonblocking_errors_size = ERRORS_STACK_INIT_SIZE;
89
92
  }
90
93
 
91
94
  static void nogvl_cleanup(DBPROCESS *client) {
92
95
  GET_CLIENT_USERDATA(client);
93
96
  userdata->nonblocking = 0;
97
+ userdata->timing_out = 0;
94
98
  /*
95
99
  Now that the blocking operation is done, we can finally throw any
96
100
  exceptions based on errors from SQL Server.
97
101
  */
98
- if (userdata->nonblocking_error.is_set) {
99
- userdata->nonblocking_error.is_set = 0;
100
- rb_tinytds_raise_error(client,
101
- userdata->nonblocking_error.is_message,
102
- userdata->nonblocking_error.cancel,
103
- userdata->nonblocking_error.error,
104
- userdata->nonblocking_error.source,
105
- userdata->nonblocking_error.severity,
106
- userdata->nonblocking_error.dberr,
107
- userdata->nonblocking_error.oserr);
102
+ for (short int i = 0; i < userdata->nonblocking_errors_length; i++) {
103
+ tinytds_errordata error = userdata->nonblocking_errors[i];
104
+
105
+ // lookahead to drain any info messages ahead of raising error
106
+ if (!error.is_message) {
107
+ for (short int j = i; j < userdata->nonblocking_errors_length; j++) {
108
+ tinytds_errordata msg_error = userdata->nonblocking_errors[j];
109
+ if (msg_error.is_message) {
110
+ rb_tinytds_raise_error(client, msg_error);
111
+ }
112
+ }
113
+ }
114
+
115
+ rb_tinytds_raise_error(client, error);
108
116
  }
117
+
118
+ free(userdata->nonblocking_errors);
119
+ userdata->nonblocking_errors_length = 0;
120
+ userdata->nonblocking_errors_size = 0;
109
121
  }
110
122
 
111
123
  static RETCODE nogvl_dbsqlok(DBPROCESS *client) {
@@ -8,7 +8,7 @@ task 'gem:windows' => ['ports:cross'] do
8
8
  build = ['bundle']
9
9
 
10
10
  # and finally build the native gem
11
- build << 'rake cross native gem RUBY_CC_VERSION=2.5.0:2.4.0:2.3.0:2.2.2:2.1.6:2.0.0 CFLAGS="-Wall" MAKE="make -j`nproc`"'
11
+ build << 'rake cross native gem RUBY_CC_VERSION=2.7.0:2.6.0:2.5.0:2.4.0 CFLAGS="-Wall" MAKE="make -j`nproc`"'
12
12
 
13
13
  RakeCompilerDock.sh build.join(' && ')
14
14
  end
data/tasks/ports.rake CHANGED
@@ -6,14 +6,14 @@ require_relative 'ports/openssl'
6
6
  require_relative 'ports/freetds'
7
7
  require_relative '../ext/tiny_tds/extconsts'
8
8
 
9
- OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE if defined? OpenSSL
10
-
11
9
  namespace :ports do
12
10
  openssl = Ports::Openssl.new(OPENSSL_VERSION)
13
11
  libiconv = Ports::Libiconv.new(ICONV_VERSION)
14
12
  freetds = Ports::Freetds.new(FREETDS_VERSION)
15
13
 
16
14
  directory "ports"
15
+ CLEAN.include "ports/*mingw32*"
16
+ CLEAN.include "ports/*.installed"
17
17
 
18
18
  task :openssl, [:host] do |task, args|
19
19
  args.with_defaults(host: RbConfig::CONFIG['host'])
@@ -71,15 +71,13 @@ namespace :ports do
71
71
  task 'cross' do
72
72
  require 'rake_compiler_dock'
73
73
 
74
- # make sure to install our bundle
75
- build = ['bundle']
76
-
77
74
  # build the ports for all our cross compile hosts
78
75
  GEM_PLATFORM_HOSTS.each do |gem_platform, host|
76
+ # make sure to install our bundle
77
+ build = ['bundle']
79
78
  build << "rake ports:compile[#{host}] MAKE='make -j`nproc`'"
79
+ RakeCompilerDock.sh build.join(' && '), platform: gem_platform
80
80
  end
81
-
82
- RakeCompilerDock.sh build.join(' && ')
83
81
  end
84
82
  end
85
83
 
@@ -27,27 +27,11 @@ module Ports
27
27
 
28
28
  private
29
29
 
30
- def execute(action, command, options={})
31
- # OpenSSL Requires Perl >= 5.10, while the Ruby devkit uses MSYS1 with Perl 5.8.8.
32
- # To overcome this, prepend Git's usr/bin to the PATH.
33
- # It has MSYS2 with a recent version of perl.
34
- prev_path = ENV['PATH']
35
- if host =~ /mingw/ && IO.popen(["perl", "-e", "print($])"], &:read).to_f < 5.010
36
- git_perl = 'C:/Program Files/Git/usr/bin'
37
- if File.directory?(git_perl)
38
- ENV['PATH'] = "#{git_perl}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
39
- ENV['PERL'] = 'perl'
40
- end
41
- end
42
-
43
- super
44
- ENV['PATH'] = prev_path
45
- end
46
-
47
30
  def configure_defaults
48
31
  opts = [
49
32
  'shared',
50
- target_arch
33
+ target_arch,
34
+ "--openssldir=#{path}",
51
35
  ]
52
36
 
53
37
  if cross_build?
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
@@ -67,10 +65,12 @@ class ClientTest < TinyTds::TestCase
67
65
  client.close if client
68
66
  end
69
67
  end unless sqlserver_azure?
70
-
71
68
  end
72
69
 
73
70
  describe 'With in-valid options' do
71
+ before(:all) do
72
+ init_toxiproxy
73
+ end
74
74
 
75
75
  it 'raises an argument error when no :host given and :dataserver is blank' do
76
76
  assert_raises(ArgumentError) { new_connection :dataserver => nil, :host => nil }
@@ -132,30 +132,46 @@ class ClientTest < TinyTds::TestCase
132
132
  end
133
133
  end
134
134
 
135
- it 'must run this test to prove we account for dropped connections' do
136
- skip
135
+ it 'raises TinyTds exception with tcp socket network failure' do
136
+ skip if ENV['CI'] && ENV['APPVEYOR_BUILD_FOLDER'] # only CI using docker
137
137
  begin
138
- client = new_connection :login_timeout => 2, :timeout => 2
138
+ client = new_connection timeout: 2, port: 1234
139
139
  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 }
144
- assert_raise_tinytds_error(action) do |e|
145
- assert_equal 20003, e.db_error_number
146
- assert_equal 6, e.severity
147
- assert_match %r{timed out}i, e.message, 'ignore if non-english test run'
140
+ action = lambda { client.execute("waitfor delay '00:00:05'").do }
141
+
142
+ # Use toxiproxy to close the TCP socket after 1 second.
143
+ # We want TinyTds to execute the statement, hit the timeout configured above, and then not be able to use the network to cancel
144
+ # the network connection needs to close after the sql batch is sent and before the timeout above is hit
145
+ Toxiproxy[:sqlserver_test].toxic(:slow_close, delay: 1000).apply do
146
+ assert_raise_tinytds_error(action) do |e|
147
+ assert_equal 20003, e.db_error_number
148
+ assert_equal 6, e.severity
149
+ assert_match %r{timed out}i, e.message, 'ignore if non-english test run'
150
+ end
148
151
  end
149
152
  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'
153
+ assert_new_connections_work
154
+ end
155
+ end
156
+
157
+ it 'raises TinyTds exception with dead connection network failure' do
158
+ skip if ENV['CI'] && ENV['APPVEYOR_BUILD_FOLDER'] # only CI using docker
159
+ begin
160
+ client = new_connection timeout: 2, port: 1234
161
+ assert_client_works(client)
162
+ action = lambda { client.execute("waitfor delay '00:00:05'").do }
163
+
164
+ # Use toxiproxy to close the network connection after 1 second.
165
+ # We want TinyTds to execute the statement, hit the timeout configured above, and then not be able to use the network to cancel
166
+ # the network connection needs to close after the sql batch is sent and before the timeout above is hit
167
+ Toxiproxy[:sqlserver_test].toxic(:timeout, timeout: 1000).apply do
168
+ assert_raise_tinytds_error(action) do |e|
169
+ assert_equal 20047, e.db_error_number
170
+ assert_includes [1,9], e.severity
171
+ assert_match %r{dead or not enabled}i, e.message, 'ignore if non-english test run'
172
+ end
157
173
  end
158
- close_client(client)
174
+ ensure
159
175
  assert_new_connections_work
160
176
  end
161
177
  end
@@ -174,57 +190,68 @@ class ClientTest < TinyTds::TestCase
174
190
 
175
191
  end
176
192
 
177
- describe 'Private methods' do
178
-
193
+ describe '#parse_username' do
179
194
  let(:client) { @client = new_connection }
180
195
 
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
196
+ it 'returns username if azure is not true' do
197
+ _(
198
+ client.send(:parse_username, username: 'user@abc123.database.windows.net')
199
+ ).must_equal 'user@abc123.database.windows.net'
184
200
  end
185
201
 
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
202
+ it 'returns short username if azure is true' do
203
+ _(
204
+ client.send(
205
+ :parse_username,
206
+ username: 'user@abc123.database.windows.net',
207
+ host: 'abc123.database.windows.net',
208
+ azure: true
209
+ )
191
210
  ).must_equal 'user@abc123'
192
211
  end
193
212
 
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
213
+ it 'returns full username if azure is false' do
214
+ _(
215
+ client.send(
216
+ :parse_username,
217
+ username: 'user@abc123.database.windows.net',
218
+ host: 'abc123.database.windows.net',
219
+ azure: false
220
+ )
199
221
  ).must_equal 'user@abc123.database.windows.net'
200
222
  end
201
223
 
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
224
+ it 'returns short username if passed and azure is true' do
225
+ _(
226
+ client.send(
227
+ :parse_username,
228
+ username: 'user@abc123',
229
+ host: 'abc123.database.windows.net',
230
+ azure: true
231
+ )
207
232
  ).must_equal 'user@abc123'
208
233
  end
209
234
 
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
235
+ it 'returns username with servername if passed and azure is true' do
236
+ _(
237
+ client.send(
238
+ :parse_username,
239
+ username: 'user',
240
+ host: 'abc123.database.windows.net',
241
+ azure: true
242
+ )
215
243
  ).must_equal 'user@abc123'
216
244
  end
217
245
 
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
246
+ it 'returns username with servername if passed and azure is false' do
247
+ _(
248
+ client.send(
249
+ :parse_username,
250
+ username: 'user',
251
+ host: 'abc123.database.windows.net',
252
+ azure: false
253
+ )
223
254
  ).must_equal 'user'
224
255
  end
225
-
226
256
  end
227
-
228
-
229
257
  end
230
-
data/test/gem_test.rb CHANGED
@@ -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')
@@ -74,12 +74,12 @@ 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
 
@@ -89,19 +89,19 @@ class GemTest < MiniTest::Spec
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')
@@ -128,12 +128,12 @@ 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
 
@@ -144,12 +144,12 @@ class GemTest < MiniTest::Spec
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
@@ -169,7 +169,7 @@ class GemTest < MiniTest::Spec
169
169
  end
170
170
 
171
171
  it "should return a #{expected} ports host" do
172
- TinyTds::Gem.ports_host.must_equal expected
172
+ _(TinyTds::Gem.ports_host).must_equal expected
173
173
  end
174
174
  end
175
175
  end