tiny_tds 2.1.3-x64-mingw32 → 2.1.4.pre-x64-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +5 -1
- data/VERSION +1 -1
- data/appveyor.yml +13 -1
- data/ext/tiny_tds/client.c +74 -30
- data/ext/tiny_tds/client.h +4 -2
- data/ext/tiny_tds/result.c +18 -9
- data/test/client_test.rb +6 -9
- data/test/result_test.rb +18 -1
- data/test/test_helper.rb +40 -3
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9d21aa286ec1e3be47f2a80b776f1a3b50aa118fa8e9b441897cc83457308523
|
4
|
+
data.tar.gz: e655d5cc98d23af01089fdce4d2d83d5cfe5088b47ad0e8816186299342e5a75
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a396a55809900ef33ea42b3c67f2f78e2747ad7759fdabd362564b83e25a6737be7419576aecfa4148504f4fba7aed32079712483c7210928ab7b8664b609b27
|
7
|
+
data.tar.gz: 2193592e960cd22d8e85a9f929550b3bcde916959dacc6dc4d4c187fc47a7437aa350dbadf594cfded077fa57693afd52f872cbd1478d2e2dac6a1e1555e431f
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -110,7 +110,7 @@ Creating a new client takes a hash of options. For valid iconv encoding options,
|
|
110
110
|
* :appname - Short string seen in SQL Servers process/activity window.
|
111
111
|
* :tds_version - TDS version. Defaults to "7.3".
|
112
112
|
* :login_timeout - Seconds to wait for login. Default to 60 seconds.
|
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.
|
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).
|
114
114
|
* :encoding - Any valid iconv value like CP1251 or ISO-8859-1. Default UTF-8.
|
115
115
|
* :azure - Pass true to signal that you are connecting to azure.
|
116
116
|
* :contained - Pass true to signal that you are connecting with a contained database user.
|
@@ -322,6 +322,10 @@ By default row caching is turned on because the SQL Server adapter for ActiveRec
|
|
322
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.
|
323
323
|
|
324
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
|
+
|
325
329
|
## Binstubs
|
326
330
|
|
327
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/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.1.
|
1
|
+
2.1.4.pre
|
data/appveyor.yml
CHANGED
@@ -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
|
data/ext/tiny_tds/client.c
CHANGED
@@ -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
|
-
|
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
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
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
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
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
|
-
|
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));
|
data/ext/tiny_tds/client.h
CHANGED
@@ -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
|
-
|
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
|
|
data/ext/tiny_tds/result.c
CHANGED
@@ -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
|
-
|
99
|
-
|
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
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
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) {
|
data/test/client_test.rb
CHANGED
@@ -129,14 +129,12 @@ class ClientTest < TinyTds::TestCase
|
|
129
129
|
end
|
130
130
|
end
|
131
131
|
|
132
|
-
it '
|
133
|
-
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
|
134
134
|
begin
|
135
|
-
client = new_connection :
|
135
|
+
client = new_connection timeout: 2
|
136
136
|
assert_client_works(client)
|
137
|
-
|
138
|
-
sleep 10
|
139
|
-
STDOUT.puts "This should not get stuck past 6 seconds!"
|
137
|
+
docker_container('pause', wait_for: 1)
|
140
138
|
action = lambda { client.execute('SELECT 1 as [one]').each }
|
141
139
|
assert_raise_tinytds_error(action) do |e|
|
142
140
|
assert_equal 20003, e.db_error_number
|
@@ -144,12 +142,11 @@ class ClientTest < TinyTds::TestCase
|
|
144
142
|
assert_match %r{timed out}i, e.message, 'ignore if non-english test run'
|
145
143
|
end
|
146
144
|
ensure
|
147
|
-
|
148
|
-
sleep 10
|
145
|
+
docker_container('unpause', wait_for: 1)
|
149
146
|
action = lambda { client.execute('SELECT 1 as [one]').each }
|
150
147
|
assert_raise_tinytds_error(action) do |e|
|
151
148
|
assert_equal 20047, e.db_error_number
|
152
|
-
|
149
|
+
assert_includes [1,9], e.severity
|
153
150
|
assert_match %r{dead or not enabled}i, e.message, 'ignore if non-english test run'
|
154
151
|
end
|
155
152
|
close_client(client)
|
data/test/result_test.rb
CHANGED
@@ -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
|
@@ -770,4 +788,3 @@ class ResultTest < TinyTds::TestCase
|
|
770
788
|
end
|
771
789
|
|
772
790
|
end
|
773
|
-
|
data/test/test_helper.rb
CHANGED
@@ -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
|
-
|
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.
|
4
|
+
version: 2.1.4.pre
|
5
5
|
platform: x64-mingw32
|
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:
|
13
|
+
date: 2021-01-19 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: mini_portile2
|
@@ -225,9 +225,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
225
225
|
version: 2.8.dev
|
226
226
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
227
227
|
requirements:
|
228
|
-
- - "
|
228
|
+
- - ">"
|
229
229
|
- !ruby/object:Gem::Version
|
230
|
-
version:
|
230
|
+
version: 1.3.1
|
231
231
|
requirements: []
|
232
232
|
rubygems_version: 3.1.2
|
233
233
|
signing_key:
|