tiny_tds 2.1.3 → 2.1.4.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +0 -0
  3. data/.gitattributes +0 -0
  4. data/.gitignore +0 -0
  5. data/.rubocop.yml +0 -0
  6. data/.travis.yml +0 -0
  7. data/CHANGELOG.md +7 -0
  8. data/CODE_OF_CONDUCT.md +0 -0
  9. data/Gemfile +0 -0
  10. data/ISSUE_TEMPLATE.md +0 -0
  11. data/MIT-LICENSE +0 -0
  12. data/README.md +5 -2
  13. data/Rakefile +0 -0
  14. data/VERSION +1 -1
  15. data/appveyor.yml +13 -1
  16. data/ext/tiny_tds/client.c +74 -30
  17. data/ext/tiny_tds/client.h +4 -2
  18. data/ext/tiny_tds/extconf.rb +0 -0
  19. data/ext/tiny_tds/extconsts.rb +0 -0
  20. data/ext/tiny_tds/result.c +18 -9
  21. data/ext/tiny_tds/result.h +0 -0
  22. data/ext/tiny_tds/tiny_tds_ext.c +0 -0
  23. data/ext/tiny_tds/tiny_tds_ext.h +0 -0
  24. data/lib/tiny_tds.rb +0 -0
  25. data/lib/tiny_tds/bin.rb +0 -0
  26. data/lib/tiny_tds/client.rb +0 -0
  27. data/lib/tiny_tds/error.rb +0 -0
  28. data/lib/tiny_tds/gem.rb +0 -0
  29. data/lib/tiny_tds/result.rb +0 -0
  30. data/lib/tiny_tds/version.rb +0 -0
  31. data/patches/freetds/1.00.27/0001-mingw_missing_inet_pton.diff +0 -0
  32. data/patches/freetds/1.00.27/0002-Don-t-use-MSYS2-file-libws2_32.diff +0 -0
  33. data/patches/libiconv/1.14/1-avoid-gets-error.patch +0 -0
  34. data/tasks/native_gem.rake +0 -0
  35. data/tasks/package.rake +0 -0
  36. data/tasks/ports.rake +0 -0
  37. data/tasks/ports/freetds.rb +0 -0
  38. data/tasks/ports/libiconv.rb +0 -0
  39. data/tasks/ports/openssl.rb +0 -0
  40. data/tasks/ports/recipe.rb +0 -0
  41. data/tasks/test.rake +0 -0
  42. data/test/appveyor/dbsetup.ps1 +0 -0
  43. data/test/appveyor/dbsetup.sql +0 -0
  44. data/test/benchmark/query.rb +0 -0
  45. data/test/benchmark/query_odbc.rb +0 -0
  46. data/test/benchmark/query_tinytds.rb +0 -0
  47. data/test/client_test.rb +52 -47
  48. data/test/gem_test.rb +15 -15
  49. data/test/result_test.rb +59 -42
  50. data/test/schema/sqlserver_2000.sql +0 -0
  51. data/test/schema/sqlserver_2005.sql +0 -0
  52. data/test/schema/sqlserver_2008.sql +0 -0
  53. data/test/schema/sqlserver_2014.sql +0 -0
  54. data/test/schema/sqlserver_2016.sql +0 -0
  55. data/test/schema/sqlserver_azure.sql +0 -0
  56. data/test/schema/sybase_ase.sql +0 -0
  57. data/test/schema_test.rb +12 -12
  58. data/test/test_helper.rb +41 -4
  59. data/test/thread_test.rb +1 -1
  60. data/tiny_tds.gemspec +0 -0
  61. metadata +27 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '009faf9814321046171c7dfe6a0cbf86b137f4295ae079c1b8ae47067a940d10'
4
- data.tar.gz: 6a20fcde7f6863f3e876f3484c4c93ded56511d93ac76de355c43f365758539d
3
+ metadata.gz: ef24128290a7b007a49222ec2d4307a834930f2aa6606fc9bc2b464b2d7400bb
4
+ data.tar.gz: e960731768896511f5592e4abd9768df0cf4a63ba089dd7c391bf845c0924788
5
5
  SHA512:
6
- metadata.gz: 6572f896cf1a4812b652417d1b353487d49998df7dac8518de7310123cce5c295d775e102a184355b8501a3fa3b1f9f66f892026a33fc97bfde56e3cb9912a36
7
- data.tar.gz: d05b4c50dbbe86f920a34cc6a6fc9bd20b9b27564d75b2e05f214b1600504898e22f5935ba1183f82646095a95d8bbbcba3f1fcc2548920e35d87b11c7bf0cd4
6
+ metadata.gz: 44a3b20f0bb91e24d325305fff06046cecc57d16fc6d8f8f7cc530e12689444b1f6b2c6346857cd11d779e0c30c64ebd856a0c9a9b473fdc1ffcc29edf356f30
7
+ data.tar.gz: 55c8753d62b46cb1f745f2d6b0a9a3f5e270e83f7a956dc03be540e4ed08aa1a7754c4c6462ba9ed58ad15c37c9f8a5be8f1b8236996999dec13d425cf9aadc5
File without changes
File without changes
data/.gitignore CHANGED
File without changes
File without changes
File without changes
@@ -1,5 +1,10 @@
1
1
  ## (unreleased)
2
2
 
3
+ ## 2.1.4
4
+
5
+ * todo: changelog for info messages
6
+ * todo: changelog for network timeouts
7
+
3
8
  ## 2.1.3
4
9
 
5
10
  * Removed old/unused appveyor config
@@ -215,6 +220,8 @@ Use both dbsetversion() vs. dbsetlversion. Partially reverts #62.
215
220
  state of the client and the need to use Result#cancel to stop processing active results. It is also
216
221
  safe to call Result#cancel over and over again.
217
222
  * Look for the syb headers only.
223
+ * Fix minitest global matchers warnings.
224
+ * Fix test warnings.
218
225
 
219
226
 
220
227
  ## 0.3.2
File without changes
data/Gemfile CHANGED
File without changes
File without changes
File without changes
data/README.md CHANGED
@@ -3,7 +3,6 @@
3
3
  * [![TravisCI](https://travis-ci.org/rails-sqlserver/tiny_tds.svg?branch=master)](https://travis-ci.org/rails-sqlserver/tiny_tds) - TravisCI
4
4
  * [![Build Status](https://ci.appveyor.com/api/projects/status/g2bhhbsdkx0mal55/branch/master?svg=true)](https://ci.appveyor.com/project/rails-sqlserver/tiny-tds/branch/master) - Appveyor
5
5
  * [![Gem Version](https://img.shields.io/gem/v/tiny_tds.svg)](https://rubygems.org/gems/tiny_tds) - Gem Version
6
- * [![Dependency Status](https://dependencyci.com/github/rails-sqlserver/tiny_tds/badge)](https://dependencyci.com/github/rails-sqlserver/tiny_tds) - Dependency Status
7
6
  * [![Gitter chat](https://img.shields.io/badge/%E2%8A%AA%20GITTER%20-JOIN%20CHAT%20%E2%86%92-brightgreen.svg?style=flat)](https://gitter.im/rails-sqlserver/activerecord-sqlserver-adapter) - Community
8
7
 
9
8
 
@@ -111,7 +110,7 @@ Creating a new client takes a hash of options. For valid iconv encoding options,
111
110
  * :appname - Short string seen in SQL Servers process/activity window.
112
111
  * :tds_version - TDS version. Defaults to "7.3".
113
112
  * :login_timeout - Seconds to wait for login. Default to 60 seconds.
114
- * :timeout - Seconds to wait for a response to a SQL command. Default 5 seconds. Prior to 1.0rc5, FreeTDS was unable to set the timeout on a per-client basis, permitting only a global timeout value. This means that if you're using an older version, the timeout values for all clients will be overwritten each time you instantiate a new `TinyTds::Client` object. If you are using 1.0rc5 or later, all clients will have an independent timeout setting as you'd expect.
113
+ * :timeout - Seconds to wait for a response to a SQL command. Default 5 seconds. Prior to 1.0rc5, FreeTDS was unable to set the timeout on a per-client basis, permitting only a global timeout value. This means that if you're using an older version, the timeout values for all clients will be overwritten each time you instantiate a new `TinyTds::Client` object. If you are using 1.0rc5 or later, all clients will have an independent timeout setting as you'd expect. Timeouts caused by network failure will raise a timeout error 1 second after the configured timeout limit is hit (see [#481](https://github.com/rails-sqlserver/tiny_tds/pull/481) for details).
115
114
  * :encoding - Any valid iconv value like CP1251 or ISO-8859-1. Default UTF-8.
116
115
  * :azure - Pass true to signal that you are connecting to azure.
117
116
  * :contained - Pass true to signal that you are connecting with a contained database user.
@@ -323,6 +322,10 @@ By default row caching is turned on because the SQL Server adapter for ActiveRec
323
322
  TinyTDS takes an opinionated stance on how we handle encoding errors. First, we treat errors differently on reads vs. writes. Our opinion is that if you are reading bad data due to your client's encoding option, you would rather just find `?` marks in your strings vs being blocked with exceptions. This is how things wold work via ODBC or SMS. On the other hand, writes will raise an exception. In this case we raise the SYBEICONVO/2402 error message which has a description of `Error converting characters into server's character set. Some character(s) could not be converted.`. Even though the severity of this message is only a `4` and TinyTDS will automatically strip/ignore unknown characters, we feel you should know that you are inserting bad encodings. In this way, a transaction can be rolled back, etc. Remember, any database write that has bad characters due to the client encoding will still be written to the database, but it is up to you rollback said write if needed. Most ORMs like ActiveRecord handle this scenario just fine.
324
323
 
325
324
 
325
+ ## Timeout Error Handling
326
+
327
+ TinyTDS will raise a `TinyTDS::Error` when a timeout is reached based on the options supplied to the client. Depending on the reason for the timeout, the connection could be dead or alive. When db processing is the cause for the timeout, the connection should still be usable after the error is raised. When network failure is the cause of the timeout, the connection will be dead. If you attempt to execute another command batch on a dead connection you will see a `DBPROCESS is dead or not enabled` error. Therefore, it is recommended to check for a `dead?` connection before trying to execute another command batch.
328
+
326
329
  ## Binstubs
327
330
 
328
331
  The TinyTDS gem uses binstub wrappers which mirror compiled [FreeTDS Utilities](http://www.freetds.org/userguide/usefreetds.htm) binaries. These native executables are usually installed at the system level when installing FreeTDS. However, when using MiniPortile to install TinyTDS as we do with Windows binaries, these binstubs will find and prefer local gem `exe` directory executables. These are the following binstubs we wrap.
data/Rakefile CHANGED
File without changes
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.1.3
1
+ 2.1.4.pre
@@ -19,7 +19,19 @@ install:
19
19
  - perl --version
20
20
  - ruby --version
21
21
  - gem --version
22
-
22
+ # Update keyring according to https://www.msys2.org/news/#2020-06-29-new-packagers
23
+ - C:\msys64\usr\bin\curl -O http://repo.msys2.org/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz
24
+ - C:\msys64\usr\bin\curl -O http://repo.msys2.org/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig
25
+ - ridk exec bash -c "pacman-key --verify msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig"
26
+ - ridk exec bash -c "pacman -U --noconfirm --config <(echo) msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz"
27
+ # Update zstd and pacman first https://github.com/msys2/MSYS2-packages/issues/2300
28
+ - C:\msys64\usr\bin\pacman --noconfirm --upgrade https://repo.msys2.org/msys/x86_64/zstd-1.4.7-1-x86_64.pkg.tar.xz # Must come First, or else pacman will install 1.4.8
29
+ - C:\msys64\usr\bin\pacman --noconfirm --upgrade https://repo.msys2.org/msys/x86_64/pacman-5.2.2-5-x86_64.pkg.tar.xz
30
+ # update packages
31
+ - C:\msys64\usr\bin\pacman --noconfirm --ask 20 --sync --refresh --refresh --sysupgrade --sysupgrade
32
+ # Kill all running msys2 binaries to avoid error "size of shared memory region changed".
33
+ # See https://github.com/msys2/MSYS2-packages/issues/258
34
+ - powershell -Command "Get-Process | Where-Object {$_.path -like 'C:\msys64*'} | Stop-Process"
23
35
  # prevent freetds to link to wrong ws2_32 lib on i686-w64-mingw32
24
36
  - c:/msys64/usr/bin/rm /usr/lib/w32api/libws2_32.a
25
37
  # Set up project prerequisits
@@ -57,6 +57,16 @@ VALUE rb_tinytds_raise_error(DBPROCESS *dbproc, int is_message, int cancel, cons
57
57
 
58
58
 
59
59
  // Lib Backend (Memory Management & Handlers)
60
+ static void push_userdata_error(tinytds_client_userdata *userdata, tinytds_errordata error) {
61
+ // reallocate memory for the array as needed
62
+ if (userdata->nonblocking_errors_size == userdata->nonblocking_errors_length) {
63
+ userdata->nonblocking_errors_size *= 2;
64
+ userdata->nonblocking_errors = realloc(userdata->nonblocking_errors, userdata->nonblocking_errors_size * sizeof(tinytds_errordata));
65
+ }
66
+
67
+ userdata->nonblocking_errors[userdata->nonblocking_errors_length] = error;
68
+ userdata->nonblocking_errors_length++;
69
+ }
60
70
 
61
71
  int tinytds_err_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr) {
62
72
  static const char *source = "error";
@@ -86,7 +96,13 @@ int tinytds_err_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, c
86
96
  but we don't ever want to automatically retry. Instead have the app
87
97
  decide what to do.
88
98
  */
89
- return_value = INT_TIMEOUT;
99
+ if (userdata->timing_out) {
100
+ return INT_CANCEL;
101
+ }
102
+ else {
103
+ userdata->timing_out = 1;
104
+ return_value = INT_TIMEOUT;
105
+ }
90
106
  cancel = 1;
91
107
  break;
92
108
 
@@ -111,24 +127,16 @@ int tinytds_err_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, c
111
127
  userdata->dbcancel_sent = 1;
112
128
  }
113
129
 
114
- /*
115
- If we've already captured an error message, don't overwrite it. This is
116
- here because FreeTDS sends a generic "General SQL Server error" message
117
- that will overwrite the real message. This is not normally a problem
118
- because a ruby exception is normally thrown and we bail before the
119
- generic message can be sent.
120
- */
121
- if (!userdata->nonblocking_error.is_set) {
122
- userdata->nonblocking_error.is_message = 0;
123
- userdata->nonblocking_error.cancel = cancel;
124
- strncpy(userdata->nonblocking_error.error, dberrstr, ERROR_MSG_SIZE);
125
- strncpy(userdata->nonblocking_error.source, source, ERROR_MSG_SIZE);
126
- userdata->nonblocking_error.severity = severity;
127
- userdata->nonblocking_error.dberr = dberr;
128
- userdata->nonblocking_error.oserr = oserr;
129
- userdata->nonblocking_error.is_set = 1;
130
- }
131
-
130
+ tinytds_errordata error = {
131
+ .is_message = 0,
132
+ .cancel = cancel,
133
+ .severity = severity,
134
+ .dberr = dberr,
135
+ .oserr = oserr
136
+ };
137
+ strncpy(error.error, dberrstr, ERROR_MSG_SIZE);
138
+ strncpy(error.source, source, ERROR_MSG_SIZE);
139
+ push_userdata_error(userdata, error);
132
140
  } else {
133
141
  rb_tinytds_raise_error(dbproc, 0, cancel, dberrstr, source, severity, dberr, oserr);
134
142
  }
@@ -144,16 +152,21 @@ int tinytds_msg_handler(DBPROCESS *dbproc, DBINT msgno, int msgstate, int severi
144
152
 
145
153
  // See tinytds_err_handler() for info about why we do this
146
154
  if (userdata && userdata->nonblocking) {
147
- if (!userdata->nonblocking_error.is_set) {
148
- userdata->nonblocking_error.is_message = !is_message_an_error;
149
- userdata->nonblocking_error.cancel = is_message_an_error;
150
- strncpy(userdata->nonblocking_error.error, msgtext, ERROR_MSG_SIZE);
151
- strncpy(userdata->nonblocking_error.source, source, ERROR_MSG_SIZE);
152
- userdata->nonblocking_error.severity = severity;
153
- userdata->nonblocking_error.dberr = msgno;
154
- userdata->nonblocking_error.oserr = msgstate;
155
- userdata->nonblocking_error.is_set = 1;
156
- }
155
+ /*
156
+ In the case of non-blocking command batch execution we can receive multiple messages
157
+ (including errors). We keep track of those here so they can be processed once the
158
+ non-blocking call returns.
159
+ */
160
+ tinytds_errordata error = {
161
+ .is_message = !is_message_an_error,
162
+ .cancel = is_message_an_error,
163
+ .severity = severity,
164
+ .dberr = msgno,
165
+ .oserr = msgstate
166
+ };
167
+ strncpy(error.error, msgtext, ERROR_MSG_SIZE);
168
+ strncpy(error.source, source, ERROR_MSG_SIZE);
169
+ push_userdata_error(userdata, error);
157
170
 
158
171
  if (is_message_an_error && !dbdead(dbproc) && !userdata->closed) {
159
172
  dbcancel(dbproc);
@@ -165,13 +178,43 @@ int tinytds_msg_handler(DBPROCESS *dbproc, DBINT msgno, int msgstate, int severi
165
178
  return 0;
166
179
  }
167
180
 
181
+ /*
182
+ Used by dbsetinterrupt -
183
+ This gets called periodically while waiting on a read from the server
184
+ Right now, we only care about cases where a read from the server is
185
+ taking longer than the specified timeout and dbcancel is not working.
186
+ In these cases we decide that we actually want to handle the interrupt
187
+ */
188
+ static int check_interrupt(void *ptr) {
189
+ GET_CLIENT_USERDATA((DBPROCESS *)ptr);
190
+ return userdata->timing_out;
191
+ }
192
+
193
+ /*
194
+ Used by dbsetinterrupt -
195
+ This gets called if check_interrupt returns TRUE.
196
+ Right now, this is only used in cases where a read from the server is
197
+ taking longer than the specified timeout and dbcancel is not working.
198
+ Return INT_CANCEL to abort the current command batch.
199
+ */
200
+ static int handle_interrupt(void *ptr) {
201
+ GET_CLIENT_USERDATA((DBPROCESS *)ptr);
202
+ if (userdata->timing_out) {
203
+ return INT_CANCEL;
204
+ }
205
+ return INT_CONTINUE;
206
+ }
207
+
168
208
  static void rb_tinytds_client_reset_userdata(tinytds_client_userdata *userdata) {
169
209
  userdata->timing_out = 0;
170
210
  userdata->dbsql_sent = 0;
171
211
  userdata->dbsqlok_sent = 0;
172
212
  userdata->dbcancel_sent = 0;
173
213
  userdata->nonblocking = 0;
174
- userdata->nonblocking_error.is_set = 0;
214
+ // the following is mainly done for consistency since the values are reset accordingly in nogvl_setup/cleanup.
215
+ // the nonblocking_errors array does not need to be freed here. That is done as part of nogvl_cleanup.
216
+ userdata->nonblocking_errors_length = 0;
217
+ userdata->nonblocking_errors_size = 0;
175
218
  }
176
219
 
177
220
  static void rb_tinytds_client_mark(void *ptr) {
@@ -381,6 +424,7 @@ static VALUE rb_tinytds_connect(VALUE self, VALUE opts) {
381
424
  }
382
425
  }
383
426
  dbsetuserdata(cwrap->client, (BYTE*)cwrap->userdata);
427
+ dbsetinterrupt(cwrap->client, check_interrupt, handle_interrupt);
384
428
  cwrap->userdata->closed = 0;
385
429
  if (!NIL_P(database) && (azure != Qtrue)) {
386
430
  dbuse(cwrap->client, StringValueCStr(database));
@@ -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
 
File without changes
File without changes
@@ -86,26 +86,35 @@ 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;
102
+ for (short int i = 0; i < userdata->nonblocking_errors_length; i++) {
103
+ tinytds_errordata error = userdata->nonblocking_errors[i];
100
104
  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);
105
+ error.is_message,
106
+ error.cancel,
107
+ error.error,
108
+ error.source,
109
+ error.severity,
110
+ error.dberr,
111
+ error.oserr
112
+ );
108
113
  }
114
+
115
+ free(userdata->nonblocking_errors);
116
+ userdata->nonblocking_errors_length = 0;
117
+ userdata->nonblocking_errors_size = 0;
109
118
  }
110
119
 
111
120
  static RETCODE nogvl_dbsqlok(DBPROCESS *client) {
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -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,7 +65,6 @@ 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
@@ -132,14 +129,12 @@ class ClientTest < TinyTds::TestCase
132
129
  end
133
130
  end
134
131
 
135
- it 'must run this test to prove we account for dropped connections' do
136
- skip
132
+ it 'raises TinyTds exception with sql batch timeout due to network failure' do
133
+ skip if ENV['CI'] && ENV['APPVEYOR_BUILD_FOLDER'] # only CI using docker
137
134
  begin
138
- client = new_connection :login_timeout => 2, :timeout => 2
135
+ client = new_connection timeout: 2
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!"
137
+ docker_container('pause', wait_for: 1)
143
138
  action = lambda { client.execute('SELECT 1 as [one]').each }
144
139
  assert_raise_tinytds_error(action) do |e|
145
140
  assert_equal 20003, e.db_error_number
@@ -147,12 +142,11 @@ class ClientTest < TinyTds::TestCase
147
142
  assert_match %r{timed out}i, e.message, 'ignore if non-english test run'
148
143
  end
149
144
  ensure
150
- STDOUT.puts "Reconnect network!"
151
- sleep 10
145
+ docker_container('unpause', wait_for: 1)
152
146
  action = lambda { client.execute('SELECT 1 as [one]').each }
153
147
  assert_raise_tinytds_error(action) do |e|
154
148
  assert_equal 20047, e.db_error_number
155
- assert_equal 1, e.severity
149
+ assert_includes [1,9], e.severity
156
150
  assert_match %r{dead or not enabled}i, e.message, 'ignore if non-english test run'
157
151
  end
158
152
  close_client(client)
@@ -174,57 +168,68 @@ class ClientTest < TinyTds::TestCase
174
168
 
175
169
  end
176
170
 
177
- describe 'Private methods' do
178
-
171
+ describe '#parse_username' do
179
172
  let(:client) { @client = new_connection }
180
173
 
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
174
+ it 'returns username if azure is not true' do
175
+ _(
176
+ client.send(:parse_username, username: 'user@abc123.database.windows.net')
177
+ ).must_equal 'user@abc123.database.windows.net'
184
178
  end
185
179
 
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
180
+ it 'returns short username if azure is true' do
181
+ _(
182
+ client.send(
183
+ :parse_username,
184
+ username: 'user@abc123.database.windows.net',
185
+ host: 'abc123.database.windows.net',
186
+ azure: true
187
+ )
191
188
  ).must_equal 'user@abc123'
192
189
  end
193
190
 
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
191
+ it 'returns full username if azure is false' do
192
+ _(
193
+ client.send(
194
+ :parse_username,
195
+ username: 'user@abc123.database.windows.net',
196
+ host: 'abc123.database.windows.net',
197
+ azure: false
198
+ )
199
199
  ).must_equal 'user@abc123.database.windows.net'
200
200
  end
201
201
 
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
202
+ it 'returns short username if passed and azure is true' do
203
+ _(
204
+ client.send(
205
+ :parse_username,
206
+ username: 'user@abc123',
207
+ host: 'abc123.database.windows.net',
208
+ azure: true
209
+ )
207
210
  ).must_equal 'user@abc123'
208
211
  end
209
212
 
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
213
+ it 'returns username with servername if passed and azure is true' do
214
+ _(
215
+ client.send(
216
+ :parse_username,
217
+ username: 'user',
218
+ host: 'abc123.database.windows.net',
219
+ azure: true
220
+ )
215
221
  ).must_equal 'user@abc123'
216
222
  end
217
223
 
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
224
+ it 'returns username with servername if passed and azure is false' do
225
+ _(
226
+ client.send(
227
+ :parse_username,
228
+ username: 'user',
229
+ host: 'abc123.database.windows.net',
230
+ azure: false
231
+ )
223
232
  ).must_equal 'user'
224
233
  end
225
-
226
234
  end
227
-
228
-
229
235
  end
230
-
@@ -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
@@ -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
@@ -215,34 +215,34 @@ class ResultTest < TinyTds::TestCase
215
215
  @client.execute("DELETE FROM [datatypes]").do
216
216
  @client.execute("ROLLBACK TRANSACTION").do
217
217
  count = @client.execute("SELECT COUNT(*) AS [count] FROM [datatypes]").each.first['count']
218
- 0.wont_equal count
218
+ _(count).wont_equal 0
219
219
  end
220
220
 
221
221
  it 'has a #fields accessor with logic default and valid outcome' do
222
222
  result = @client.execute(@query1)
223
- result.fields.must_equal ['one']
223
+ _(result.fields).must_equal ['one']
224
224
  result.each
225
- result.fields.must_equal ['one']
225
+ _(result.fields).must_equal ['one']
226
226
  end
227
227
 
228
228
  it 'always returns an array for fields for all sql' do
229
229
  result = @client.execute("USE [tinytdstest]")
230
- result.fields.must_equal []
230
+ _(result.fields).must_equal []
231
231
  result.do
232
- result.fields.must_equal []
232
+ _(result.fields).must_equal []
233
233
  end
234
234
 
235
235
  it 'returns fields even when no results are found' do
236
236
  no_results_query = "SELECT [id], [varchar_50] FROM [datatypes] WHERE [varchar_50] = 'NOTFOUND'"
237
237
  # Fields before each.
238
238
  result = @client.execute(no_results_query)
239
- result.fields.must_equal ['id','varchar_50']
239
+ _(result.fields).must_equal ['id','varchar_50']
240
240
  result.each
241
- result.fields.must_equal ['id','varchar_50']
241
+ _(result.fields).must_equal ['id','varchar_50']
242
242
  # Each then fields
243
243
  result = @client.execute(no_results_query)
244
244
  result.each
245
- result.fields.must_equal ['id','varchar_50']
245
+ _(result.fields).must_equal ['id','varchar_50']
246
246
  end
247
247
 
248
248
  it 'allows the result to be canceled before reading' do
@@ -254,27 +254,27 @@ class ResultTest < TinyTds::TestCase
254
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
255
  # Default state.
256
256
  @client = TinyTds::Client.new(connection_options)
257
- @client.sqlsent?.must_equal false
258
- @client.canceled?.must_equal false
257
+ _(@client.sqlsent?).must_equal false
258
+ _(@client.canceled?).must_equal false
259
259
  # With active result before and after cancel.
260
260
  result = @client.execute(@query1)
261
- @client.sqlsent?.must_equal true
262
- @client.canceled?.must_equal false
261
+ _(@client.sqlsent?).must_equal true
262
+ _(@client.canceled?).must_equal false
263
263
  result.cancel
264
- @client.sqlsent?.must_equal false
265
- @client.canceled?.must_equal true
264
+ _(@client.sqlsent?).must_equal false
265
+ _(@client.canceled?).must_equal true
266
266
  assert result.cancel, 'must be safe to call again'
267
267
  # With each and no block.
268
268
  @client.execute(@query1).each
269
- @client.sqlsent?.must_equal false
270
- @client.canceled?.must_equal false
269
+ _(@client.sqlsent?).must_equal false
270
+ _(@client.canceled?).must_equal false
271
271
  # With each and block.
272
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
273
+ _(@client.sqlsent?).must_equal true, 'when iterating over each row in a block'
274
+ _(@client.canceled?).must_equal false
275
275
  end
276
- @client.sqlsent?.must_equal false
277
- @client.canceled?.must_equal false
276
+ _(@client.sqlsent?).must_equal false
277
+ _(@client.canceled?).must_equal false
278
278
  # With each and block canceled half way thru.
279
279
  count = @client.execute("SELECT COUNT([id]) AS [count] FROM [datatypes]").each[0]['count']
280
280
  assert count > 10, 'since we want to cancel early for test'
@@ -284,25 +284,25 @@ class ResultTest < TinyTds::TestCase
284
284
  break if index > 10
285
285
  index += 1
286
286
  end
287
- @client.sqlsent?.must_equal true
288
- @client.canceled?.must_equal false
287
+ _(@client.sqlsent?).must_equal true
288
+ _(@client.canceled?).must_equal false
289
289
  result.cancel
290
- @client.sqlsent?.must_equal false
291
- @client.canceled?.must_equal true
290
+ _(@client.sqlsent?).must_equal false
291
+ _(@client.canceled?).must_equal true
292
292
  # With do method.
293
293
  @client.execute(@query1).do
294
- @client.sqlsent?.must_equal false
295
- @client.canceled?.must_equal true
294
+ _(@client.sqlsent?).must_equal false
295
+ _(@client.canceled?).must_equal true
296
296
  # With insert method.
297
297
  rollback_transaction(@client) do
298
298
  @client.execute("INSERT INTO [datatypes] ([varchar_50]) VALUES ('test')").insert
299
- @client.sqlsent?.must_equal false
300
- @client.canceled?.must_equal true
299
+ _(@client.sqlsent?).must_equal false
300
+ _(@client.canceled?).must_equal true
301
301
  end
302
302
  # With first
303
303
  @client.execute("SELECT [id] FROM [datatypes]").each(:first => true)
304
- @client.sqlsent?.must_equal false
305
- @client.canceled?.must_equal true
304
+ _(@client.sqlsent?).must_equal false
305
+ _(@client.canceled?).must_equal true
306
306
  end
307
307
 
308
308
  it 'use same string object for hash keys' do
@@ -337,7 +337,7 @@ class ResultTest < TinyTds::TestCase
337
337
 
338
338
  it 'with LOGINPROPERTY function' do
339
339
  v = @client.execute("SELECT LOGINPROPERTY('sa', 'IsLocked') as v").first['v']
340
- v.must_equal 0
340
+ _(v).must_equal 0
341
341
  end
342
342
 
343
343
  describe 'with multiple result sets' do
@@ -652,6 +652,24 @@ class ResultTest < TinyTds::TestCase
652
652
  assert_equal 1, messages.length, 'there should be one message after one print statement'
653
653
  assert_equal msg, m.message, 'message text'
654
654
  end
655
+
656
+ it 'must raise an error preceded by a `print` message' do
657
+ messages.clear
658
+ action = lambda { @client.execute("EXEC tinytds_TestPrintWithError").do }
659
+ assert_raise_tinytds_error(action) do |e|
660
+ assert_equal 'hello', messages.first.message, 'message text'
661
+
662
+ assert_equal "Error following print", e.message
663
+ assert_equal 16, e.severity
664
+ assert_equal 50000, e.db_error_number
665
+ end
666
+ end
667
+
668
+ it 'calls the provided message handler for each of a series of `print` messages' do
669
+ messages.clear
670
+ @client.execute("EXEC tinytds_TestSeveralPrints").do
671
+ assert_equal ['hello 1', 'hello 2', 'hello 3'], messages.map { |e| e.message }, 'message list'
672
+ end
655
673
  end
656
674
 
657
675
  it 'must not raise an error when severity is 10 or less' do
@@ -684,7 +702,7 @@ class ResultTest < TinyTds::TestCase
684
702
  end
685
703
 
686
704
  it 'throws an error when you execute another query with other results pending' do
687
- result1 = @client.execute(@query1)
705
+ @client.execute(@query1)
688
706
  action = lambda { @client.execute(@query1) }
689
707
  assert_raise_tinytds_error(action) do |e|
690
708
  assert_match %r|with results pending|i, e.message
@@ -717,22 +735,22 @@ class ResultTest < TinyTds::TestCase
717
735
  it 'must not error at all from reading non-convertable charcters and just use ? marks' do
718
736
  close_client
719
737
  @client = new_connection :encoding => 'ASCII'
720
- @client.charset.must_equal 'ASCII'
721
- find_value(202, :nvarchar_50).must_equal 'test nvarchar_50 ??'
738
+ _(@client.charset).must_equal 'ASCII'
739
+ _(find_value(202, :nvarchar_50)).must_equal 'test nvarchar_50 ??'
722
740
  end
723
741
 
724
742
  it 'must error gracefully from writing non-convertable characters' do
725
743
  close_client
726
744
  @client = new_connection :encoding => 'ASCII'
727
- @client.charset.must_equal 'ASCII'
745
+ _(@client.charset).must_equal 'ASCII'
728
746
  rollback_transaction(@client) do
729
747
  text = 'Test ✓'
730
748
  @client.execute("DELETE FROM [datatypes] WHERE [nvarchar_50] IS NOT NULL").do
731
749
  action = lambda { @client.execute("INSERT INTO [datatypes] ([nvarchar_50]) VALUES ('#{text}')").do }
732
750
  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
751
+ _(e.message).must_match %r{Unclosed quotation mark}i
752
+ _(e.severity).must_equal 15
753
+ _(e.db_error_number).must_equal 105
736
754
  end
737
755
  assert_followup_query
738
756
  end
@@ -770,4 +788,3 @@ class ResultTest < TinyTds::TestCase
770
788
  end
771
789
 
772
790
  end
773
-
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -14,7 +14,7 @@ class SchemaTest < TinyTds::TestCase
14
14
  describe 'for shared types' do
15
15
 
16
16
  it 'casts bigint' do
17
- assert_equal -9223372036854775807, find_value(11, :bigint)
17
+ assert_equal (-9223372036854775807), find_value(11, :bigint)
18
18
  assert_equal 9223372036854775806, find_value(12, :bigint)
19
19
  end
20
20
 
@@ -96,7 +96,7 @@ class SchemaTest < TinyTds::TestCase
96
96
  end
97
97
 
98
98
  it 'casts int' do
99
- assert_equal -2147483647, find_value(151, :int)
99
+ assert_equal (-2147483647), find_value(151, :int)
100
100
  assert_equal 2147483646, find_value(152, :int)
101
101
  end
102
102
 
@@ -170,7 +170,7 @@ class SchemaTest < TinyTds::TestCase
170
170
  end
171
171
 
172
172
  it 'casts smallint' do
173
- assert_equal -32767, find_value(241, :smallint)
173
+ assert_equal (-32767), find_value(241, :smallint)
174
174
  assert_equal 32766, find_value(242, :smallint)
175
175
  end
176
176
 
@@ -396,15 +396,15 @@ class SchemaTest < TinyTds::TestCase
396
396
  if @client.tds_73?
397
397
  assertions = lambda {
398
398
  assert_instance_of Time, v
399
- assert_equal 1984, v.year, 'Year'
400
- assert_equal 1, v.month, 'Month'
401
- assert_equal 24, v.day, 'Day'
402
- assert_equal 4, v.hour, 'Hour'
403
- assert_equal 20, v.min, 'Minute'
404
- assert_equal 59, v.sec, 'Second'
405
- assert_equal 123456, v.usec, 'Microseconds'
406
- assert_equal 123456700, v.nsec, 'Nanoseconds'
407
- assert_equal -28800, v.utc_offset, 'Offset'
399
+ assert_equal 1984, v.year, 'Year'
400
+ assert_equal 1, v.month, 'Month'
401
+ assert_equal 24, v.day, 'Day'
402
+ assert_equal 4, v.hour, 'Hour'
403
+ assert_equal 20, v.min, 'Minute'
404
+ assert_equal 59, v.sec, 'Second'
405
+ assert_equal 123456, v.usec, 'Microseconds'
406
+ assert_equal 123456700, v.nsec, 'Nanoseconds'
407
+ assert_equal (-28800), v.utc_offset, 'Offset'
408
408
  }
409
409
  assertions.call
410
410
  v = find_value 84, :datetimeoffset_7, timezone: :local
@@ -93,7 +93,7 @@ module TinyTds
93
93
  end
94
94
 
95
95
  def assert_client_works(client)
96
- client.execute("SELECT 'client_works' as [client_works]").each.must_equal [{'client_works' => 'client_works'}]
96
+ _(client.execute("SELECT 'client_works' as [client_works]").each).must_equal [{'client_works' => 'client_works'}]
97
97
  end
98
98
 
99
99
  def assert_new_connections_work
@@ -153,6 +153,8 @@ module TinyTds
153
153
  loader.execute(drop_sql).do
154
154
  loader.execute(schema_sql).do
155
155
  loader.execute(sp_sql).do
156
+ loader.execute(sp_error_sql).do
157
+ loader.execute(sp_several_prints_sql).do
156
158
  loader.close
157
159
  true
158
160
  end
@@ -167,7 +169,16 @@ module TinyTds
167
169
  ) DROP TABLE datatypes
168
170
  IF EXISTS(
169
171
  SELECT 1 FROM sysobjects WHERE type = 'P' AND name = 'tinytds_TestReturnCodes'
170
- ) DROP PROCEDURE tinytds_TestReturnCodes|
172
+ ) DROP PROCEDURE tinytds_TestReturnCodes
173
+ IF EXISTS(
174
+ SELECT 1 FROM sysobjects WHERE type = 'P' AND name = 'tinytds_TestPrintWithError'
175
+ ) DROP PROCEDURE tinytds_TestPrintWithError
176
+ IF EXISTS(
177
+ SELECT 1 FROM sysobjects WHERE type = 'P' AND name = 'tinytds_TestPrintWithError'
178
+ ) DROP PROCEDURE tinytds_TestPrintWithError
179
+ IF EXISTS(
180
+ SELECT 1 FROM sysobjects WHERE type = 'P' AND name = 'tinytds_TestSeveralPrints'
181
+ ) DROP PROCEDURE tinytds_TestSeveralPrints|
171
182
  end
172
183
 
173
184
  def drop_sql_microsoft
@@ -181,7 +192,15 @@ module TinyTds
181
192
  IF EXISTS (
182
193
  SELECT name FROM sysobjects
183
194
  WHERE name = 'tinytds_TestReturnCodes' AND type = 'P'
184
- ) DROP PROCEDURE tinytds_TestReturnCodes|
195
+ ) DROP PROCEDURE tinytds_TestReturnCodes
196
+ IF EXISTS (
197
+ SELECT name FROM sysobjects
198
+ WHERE name = 'tinytds_TestPrintWithError' AND type = 'P'
199
+ ) DROP PROCEDURE tinytds_TestPrintWithError
200
+ IF EXISTS (
201
+ SELECT name FROM sysobjects
202
+ WHERE name = 'tinytds_TestSeveralPrints' AND type = 'P'
203
+ ) DROP PROCEDURE tinytds_TestSeveralPrints|
185
204
  end
186
205
 
187
206
  def sp_sql
@@ -191,6 +210,21 @@ module TinyTds
191
210
  RETURN(420) |
192
211
  end
193
212
 
213
+ def sp_error_sql
214
+ %|CREATE PROCEDURE tinytds_TestPrintWithError
215
+ AS
216
+ PRINT 'hello'
217
+ RAISERROR('Error following print', 16, 1)|
218
+ end
219
+
220
+ def sp_several_prints_sql
221
+ %|CREATE PROCEDURE tinytds_TestSeveralPrints
222
+ AS
223
+ PRINT 'hello 1'
224
+ PRINT 'hello 2'
225
+ PRINT 'hello 3'|
226
+ end
227
+
194
228
  def find_value(id, column, query_options={})
195
229
  query_options[:timezone] ||= :utc
196
230
  sql = "SELECT [#{column}] FROM [datatypes] WHERE [id] = #{id}"
@@ -212,6 +246,9 @@ module TinyTds
212
246
  client.execute("ROLLBACK TRANSACTION").do
213
247
  end
214
248
 
249
+ def docker_container(cmd, wait_for: 0)
250
+ system("docker #{cmd} $(docker ps --format '{{.Names}}' --filter 'ancestor=metaskills/mssql-server-linux-tinytds:2017-GA') > /dev/null")
251
+ sleep(wait_for) if wait_for > 0
252
+ end
215
253
  end
216
254
  end
217
-
@@ -49,7 +49,7 @@ class ThreadTest < TinyTds::TestCase
49
49
  begin
50
50
  result = client.execute "select dbname()"
51
51
  result.each { |r| puts r }
52
- rescue Exception => e
52
+ rescue Exception => _e
53
53
  # We are throwing an error on purpose here since 0.6.1 would
54
54
  # segfault on errors thrown in threads
55
55
  end
File without changes
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tiny_tds
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.3
4
+ version: 2.1.4.pre
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ken Collins
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2020-09-28 00:00:00.000000000 Z
13
+ date: 2021-01-19 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: mini_portile2
@@ -191,12 +191,34 @@ required_ruby_version: !ruby/object:Gem::Requirement
191
191
  version: 2.0.0
192
192
  required_rubygems_version: !ruby/object:Gem::Requirement
193
193
  requirements:
194
- - - ">="
194
+ - - ">"
195
195
  - !ruby/object:Gem::Version
196
- version: '0'
196
+ version: 1.3.1
197
197
  requirements: []
198
198
  rubygems_version: 3.0.3
199
199
  signing_key:
200
200
  specification_version: 4
201
201
  summary: TinyTDS - A modern, simple and fast FreeTDS library for Ruby using DB-Library.
202
- test_files: []
202
+ test_files:
203
+ - test/appveyor/dbsetup.ps1
204
+ - test/appveyor/dbsetup.sql
205
+ - test/benchmark/query.rb
206
+ - test/benchmark/query_odbc.rb
207
+ - test/benchmark/query_tinytds.rb
208
+ - test/bin/install-freetds.sh
209
+ - test/bin/install-openssl.sh
210
+ - test/bin/setup.sh
211
+ - test/client_test.rb
212
+ - test/gem_test.rb
213
+ - test/result_test.rb
214
+ - test/schema/1px.gif
215
+ - test/schema/sqlserver_2000.sql
216
+ - test/schema/sqlserver_2005.sql
217
+ - test/schema/sqlserver_2008.sql
218
+ - test/schema/sqlserver_2014.sql
219
+ - test/schema/sqlserver_2016.sql
220
+ - test/schema/sqlserver_azure.sql
221
+ - test/schema/sybase_ase.sql
222
+ - test/schema_test.rb
223
+ - test/test_helper.rb
224
+ - test/thread_test.rb