tiny_tds 1.0.4 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.codeclimate.yml +20 -0
- data/.gitattributes +1 -0
- data/.github/workflows/ci.yml +590 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +31 -0
- data/{CHANGELOG → CHANGELOG.md} +133 -26
- data/Gemfile +1 -5
- data/ISSUE_TEMPLATE.md +36 -3
- data/README.md +147 -85
- data/Rakefile +51 -94
- data/VERSION +1 -1
- data/docker-compose.yml +34 -0
- data/ext/tiny_tds/client.c +149 -67
- data/ext/tiny_tds/client.h +11 -5
- data/ext/tiny_tds/extconf.rb +144 -283
- data/ext/tiny_tds/extconsts.rb +4 -11
- data/ext/tiny_tds/result.c +68 -50
- data/ext/tiny_tds/tiny_tds_ext.c +4 -1
- data/lib/tiny_tds/bin.rb +44 -40
- data/lib/tiny_tds/client.rb +63 -55
- data/lib/tiny_tds/error.rb +0 -3
- data/lib/tiny_tds/gem.rb +23 -0
- data/lib/tiny_tds/result.rb +0 -3
- data/lib/tiny_tds.rb +37 -32
- data/{ports/patches/freetds/1.00 → patches/freetds/1.00.27}/0001-mingw_missing_inet_pton.diff +4 -4
- data/patches/freetds/1.00.27/0002-Don-t-use-MSYS2-file-libws2_32.diff +28 -0
- data/patches/libiconv/1.14/1-avoid-gets-error.patch +17 -0
- data/setup_cimgruby_dev.sh +25 -0
- data/start_dev.sh +21 -0
- data/tasks/native_gem.rake +16 -0
- data/tasks/package.rake +6 -0
- data/tasks/ports.rake +24 -0
- data/tasks/test.rake +7 -0
- data/test/bin/install-freetds.sh +18 -0
- data/test/bin/install-mssql.ps1 +42 -0
- data/test/bin/install-mssqltools.sh +9 -0
- data/test/bin/install-openssl.sh +18 -0
- data/test/bin/restore-from-native-gem.ps1 +10 -0
- data/test/bin/setup_tinytds_db.sh +7 -0
- data/test/bin/setup_volume_permissions.sh +10 -0
- data/test/client_test.rb +161 -112
- data/test/gem_test.rb +100 -0
- data/test/result_test.rb +293 -313
- data/test/schema_test.rb +369 -395
- data/test/sql/db-create.sql +18 -0
- data/test/sql/db-login.sql +38 -0
- data/test/test_helper.rb +116 -85
- data/test/thread_test.rb +22 -31
- data/tiny_tds.gemspec +27 -24
- metadata +109 -56
- data/appveyor.yml +0 -51
- data/test/appveyor/dbsetup.ps1 +0 -27
- data/test/appveyor/dbsetup.sql +0 -9
- data/test/benchmark/query.rb +0 -77
- data/test/benchmark/query_odbc.rb +0 -106
- data/test/benchmark/query_tinytds.rb +0 -126
- data/test/schema/sqlserver_2000.sql +0 -140
- data/test/schema/sqlserver_2005.sql +0 -140
- data/test/schema/sqlserver_2014.sql +0 -140
- data/test/schema/sybase_ase.sql +0 -138
- /data/bin/{defncopy → defncopy-ttds} +0 -0
- /data/bin/{tsql → tsql-ttds} +0 -0
- /data/test/schema/{sqlserver_2008.sql → sqlserver_2017.sql} +0 -0
data/ext/tiny_tds/client.c
CHANGED
@@ -3,9 +3,9 @@
|
|
3
3
|
|
4
4
|
VALUE cTinyTdsClient;
|
5
5
|
extern VALUE mTinyTds, cTinyTdsError;
|
6
|
-
static ID sym_username, sym_password, sym_dataserver, sym_database, sym_appname, sym_tds_version, sym_login_timeout, sym_timeout, sym_encoding, sym_azure;
|
6
|
+
static ID sym_username, sym_password, sym_dataserver, sym_database, sym_appname, sym_tds_version, sym_login_timeout, sym_timeout, sym_encoding, sym_azure, sym_contained, sym_use_utf16, sym_message_handler;
|
7
7
|
static ID intern_source_eql, intern_severity_eql, intern_db_error_number_eql, intern_os_error_number_eql;
|
8
|
-
static ID intern_new, intern_dup, intern_transpose_iconv_encoding, intern_local_offset, intern_gsub;
|
8
|
+
static ID intern_new, intern_dup, intern_transpose_iconv_encoding, intern_local_offset, intern_gsub, intern_call;
|
9
9
|
VALUE opt_escape_regex, opt_escape_dblquote;
|
10
10
|
|
11
11
|
|
@@ -24,29 +24,49 @@ VALUE opt_escape_regex, opt_escape_dblquote;
|
|
24
24
|
|
25
25
|
// Lib Backend (Helpers)
|
26
26
|
|
27
|
-
VALUE rb_tinytds_raise_error(DBPROCESS *dbproc,
|
27
|
+
VALUE rb_tinytds_raise_error(DBPROCESS *dbproc, tinytds_errordata error) {
|
28
28
|
VALUE e;
|
29
29
|
GET_CLIENT_USERDATA(dbproc);
|
30
|
-
if (cancel && !dbdead(dbproc) && userdata && !userdata->closed) {
|
30
|
+
if (error.cancel && !dbdead(dbproc) && userdata && !userdata->closed) {
|
31
31
|
userdata->dbsqlok_sent = 1;
|
32
32
|
dbsqlok(dbproc);
|
33
33
|
userdata->dbcancel_sent = 1;
|
34
34
|
dbcancel(dbproc);
|
35
35
|
}
|
36
|
-
e = rb_exc_new2(cTinyTdsError, error);
|
37
|
-
rb_funcall(e, intern_source_eql, 1, rb_str_new2(source));
|
38
|
-
if (severity)
|
39
|
-
rb_funcall(e, intern_severity_eql, 1, INT2FIX(severity));
|
40
|
-
if (dberr)
|
41
|
-
rb_funcall(e, intern_db_error_number_eql, 1, INT2FIX(dberr));
|
42
|
-
if (oserr)
|
43
|
-
rb_funcall(e, intern_os_error_number_eql, 1, INT2FIX(oserr));
|
36
|
+
e = rb_exc_new2(cTinyTdsError, error.error);
|
37
|
+
rb_funcall(e, intern_source_eql, 1, rb_str_new2(error.source));
|
38
|
+
if (error.severity)
|
39
|
+
rb_funcall(e, intern_severity_eql, 1, INT2FIX(error.severity));
|
40
|
+
if (error.dberr)
|
41
|
+
rb_funcall(e, intern_db_error_number_eql, 1, INT2FIX(error.dberr));
|
42
|
+
if (error.oserr)
|
43
|
+
rb_funcall(e, intern_os_error_number_eql, 1, INT2FIX(error.oserr));
|
44
|
+
|
45
|
+
if (error.severity <= 10 && error.is_message) {
|
46
|
+
VALUE message_handler = userdata && userdata->message_handler ? userdata->message_handler : Qnil;
|
47
|
+
if (message_handler && message_handler != Qnil && rb_respond_to(message_handler, intern_call) != 0) {
|
48
|
+
rb_funcall(message_handler, intern_call, 1, e);
|
49
|
+
}
|
50
|
+
|
51
|
+
return Qnil;
|
52
|
+
}
|
53
|
+
|
44
54
|
rb_exc_raise(e);
|
45
55
|
return Qnil;
|
46
56
|
}
|
47
57
|
|
48
58
|
|
49
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
|
+
}
|
50
70
|
|
51
71
|
int tinytds_err_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr) {
|
52
72
|
static const char *source = "error";
|
@@ -76,6 +96,13 @@ int tinytds_err_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, c
|
|
76
96
|
but we don't ever want to automatically retry. Instead have the app
|
77
97
|
decide what to do.
|
78
98
|
*/
|
99
|
+
if (userdata && userdata->timing_out) {
|
100
|
+
return INT_CANCEL;
|
101
|
+
}
|
102
|
+
// userdata will not be set if hitting timeout during login so check for it first
|
103
|
+
if (userdata) {
|
104
|
+
userdata->timing_out = 1;
|
105
|
+
}
|
79
106
|
return_value = INT_TIMEOUT;
|
80
107
|
cancel = 1;
|
81
108
|
break;
|
@@ -89,6 +116,16 @@ int tinytds_err_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, c
|
|
89
116
|
break;
|
90
117
|
}
|
91
118
|
|
119
|
+
tinytds_errordata error_data = {
|
120
|
+
.is_message = 0,
|
121
|
+
.cancel = cancel,
|
122
|
+
.severity = severity,
|
123
|
+
.dberr = dberr,
|
124
|
+
.oserr = oserr
|
125
|
+
};
|
126
|
+
strncpy(error_data.error, dberrstr, ERROR_MSG_SIZE);
|
127
|
+
strncpy(error_data.source, source, ERROR_MSG_SIZE);
|
128
|
+
|
92
129
|
/*
|
93
130
|
When in non-blocking mode we need to store the exception data to throw it
|
94
131
|
once the blocking call returns, otherwise we will segfault ruby since part
|
@@ -100,26 +137,9 @@ int tinytds_err_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, c
|
|
100
137
|
dbcancel(dbproc);
|
101
138
|
userdata->dbcancel_sent = 1;
|
102
139
|
}
|
103
|
-
|
104
|
-
/*
|
105
|
-
If we've already captured an error message, don't overwrite it. This is
|
106
|
-
here because FreeTDS sends a generic "General SQL Server error" message
|
107
|
-
that will overwrite the real message. This is not normally a problem
|
108
|
-
because a ruby exception is normally thrown and we bail before the
|
109
|
-
generic message can be sent.
|
110
|
-
*/
|
111
|
-
if (!userdata->nonblocking_error.is_set) {
|
112
|
-
userdata->nonblocking_error.cancel = cancel;
|
113
|
-
strcpy(userdata->nonblocking_error.error, dberrstr);
|
114
|
-
strcpy(userdata->nonblocking_error.source, source);
|
115
|
-
userdata->nonblocking_error.severity = severity;
|
116
|
-
userdata->nonblocking_error.dberr = dberr;
|
117
|
-
userdata->nonblocking_error.oserr = oserr;
|
118
|
-
userdata->nonblocking_error.is_set = 1;
|
119
|
-
}
|
120
|
-
|
140
|
+
push_userdata_error(userdata, error_data);
|
121
141
|
} else {
|
122
|
-
rb_tinytds_raise_error(dbproc,
|
142
|
+
rb_tinytds_raise_error(dbproc, error_data);
|
123
143
|
}
|
124
144
|
|
125
145
|
return return_value;
|
@@ -128,36 +148,75 @@ int tinytds_err_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, c
|
|
128
148
|
int tinytds_msg_handler(DBPROCESS *dbproc, DBINT msgno, int msgstate, int severity, char *msgtext, char *srvname, char *procname, int line) {
|
129
149
|
static const char *source = "message";
|
130
150
|
GET_CLIENT_USERDATA(dbproc);
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
151
|
+
|
152
|
+
int is_message_an_error = severity > 10 ? 1 : 0;
|
153
|
+
|
154
|
+
tinytds_errordata error_data = {
|
155
|
+
.is_message = !is_message_an_error,
|
156
|
+
.cancel = is_message_an_error,
|
157
|
+
.severity = severity,
|
158
|
+
.dberr = msgno,
|
159
|
+
.oserr = msgstate
|
160
|
+
};
|
161
|
+
strncpy(error_data.error, msgtext, ERROR_MSG_SIZE);
|
162
|
+
strncpy(error_data.source, source, ERROR_MSG_SIZE);
|
163
|
+
|
164
|
+
// See tinytds_err_handler() for info about why we do this
|
165
|
+
if (userdata && userdata->nonblocking) {
|
166
|
+
/*
|
167
|
+
In the case of non-blocking command batch execution we can receive multiple messages
|
168
|
+
(including errors). We keep track of those here so they can be processed once the
|
169
|
+
non-blocking call returns.
|
170
|
+
*/
|
171
|
+
push_userdata_error(userdata, error_data);
|
172
|
+
|
173
|
+
if (is_message_an_error && !dbdead(dbproc) && !userdata->closed) {
|
174
|
+
dbcancel(dbproc);
|
175
|
+
userdata->dbcancel_sent = 1;
|
149
176
|
}
|
177
|
+
} else {
|
178
|
+
rb_tinytds_raise_error(dbproc, error_data);
|
150
179
|
}
|
151
180
|
return 0;
|
152
181
|
}
|
153
182
|
|
183
|
+
/*
|
184
|
+
Used by dbsetinterrupt -
|
185
|
+
This gets called periodically while waiting on a read from the server
|
186
|
+
Right now, we only care about cases where a read from the server is
|
187
|
+
taking longer than the specified timeout and dbcancel is not working.
|
188
|
+
In these cases we decide that we actually want to handle the interrupt
|
189
|
+
*/
|
190
|
+
static int check_interrupt(void *ptr) {
|
191
|
+
GET_CLIENT_USERDATA((DBPROCESS *)ptr);
|
192
|
+
return userdata->timing_out;
|
193
|
+
}
|
194
|
+
|
195
|
+
/*
|
196
|
+
Used by dbsetinterrupt -
|
197
|
+
This gets called if check_interrupt returns TRUE.
|
198
|
+
Right now, this is only used in cases where a read from the server is
|
199
|
+
taking longer than the specified timeout and dbcancel is not working.
|
200
|
+
Return INT_CANCEL to abort the current command batch.
|
201
|
+
*/
|
202
|
+
static int handle_interrupt(void *ptr) {
|
203
|
+
GET_CLIENT_USERDATA((DBPROCESS *)ptr);
|
204
|
+
if (userdata->timing_out) {
|
205
|
+
return INT_CANCEL;
|
206
|
+
}
|
207
|
+
return INT_CONTINUE;
|
208
|
+
}
|
209
|
+
|
154
210
|
static void rb_tinytds_client_reset_userdata(tinytds_client_userdata *userdata) {
|
155
211
|
userdata->timing_out = 0;
|
156
212
|
userdata->dbsql_sent = 0;
|
157
213
|
userdata->dbsqlok_sent = 0;
|
158
214
|
userdata->dbcancel_sent = 0;
|
159
215
|
userdata->nonblocking = 0;
|
160
|
-
|
216
|
+
// the following is mainly done for consistency since the values are reset accordingly in nogvl_setup/cleanup.
|
217
|
+
// the nonblocking_errors array does not need to be freed here. That is done as part of nogvl_cleanup.
|
218
|
+
userdata->nonblocking_errors_length = 0;
|
219
|
+
userdata->nonblocking_errors_size = 0;
|
161
220
|
}
|
162
221
|
|
163
222
|
static void rb_tinytds_client_mark(void *ptr) {
|
@@ -173,6 +232,7 @@ static void rb_tinytds_client_free(void *ptr) {
|
|
173
232
|
dbloginfree(cwrap->login);
|
174
233
|
if (cwrap->client && !cwrap->closed) {
|
175
234
|
dbclose(cwrap->client);
|
235
|
+
cwrap->client = NULL;
|
176
236
|
cwrap->closed = 1;
|
177
237
|
cwrap->userdata->closed = 1;
|
178
238
|
}
|
@@ -204,6 +264,7 @@ static VALUE rb_tinytds_close(VALUE self) {
|
|
204
264
|
GET_CLIENT_WRAPPER(self);
|
205
265
|
if (cwrap->client && !cwrap->closed) {
|
206
266
|
dbclose(cwrap->client);
|
267
|
+
cwrap->client = NULL;
|
207
268
|
cwrap->closed = 1;
|
208
269
|
cwrap->userdata->closed = 1;
|
209
270
|
}
|
@@ -238,8 +299,7 @@ static VALUE rb_tinytds_execute(VALUE self, VALUE sql) {
|
|
238
299
|
REQUIRE_OPEN_CLIENT(cwrap);
|
239
300
|
dbcmd(cwrap->client, StringValueCStr(sql));
|
240
301
|
if (dbsqlsend(cwrap->client) == FAIL) {
|
241
|
-
|
242
|
-
return Qfalse;
|
302
|
+
rb_raise(cTinyTdsError, "failed dbsqlsend() function");
|
243
303
|
}
|
244
304
|
cwrap->userdata->dbsql_sent = 1;
|
245
305
|
result = rb_tinytds_new_result_obj(cwrap);
|
@@ -293,7 +353,7 @@ static VALUE rb_tinytds_identity_sql(VALUE self) {
|
|
293
353
|
|
294
354
|
static VALUE rb_tinytds_connect(VALUE self, VALUE opts) {
|
295
355
|
/* Parsing options hash to local vars. */
|
296
|
-
VALUE user, pass, dataserver, database, app, version, ltimeout, timeout, charset, azure;
|
356
|
+
VALUE user, pass, dataserver, database, app, version, ltimeout, timeout, charset, azure, contained, use_utf16;
|
297
357
|
GET_CLIENT_WRAPPER(self);
|
298
358
|
|
299
359
|
user = rb_hash_aref(opts, sym_username);
|
@@ -306,6 +366,9 @@ static VALUE rb_tinytds_connect(VALUE self, VALUE opts) {
|
|
306
366
|
timeout = rb_hash_aref(opts, sym_timeout);
|
307
367
|
charset = rb_hash_aref(opts, sym_encoding);
|
308
368
|
azure = rb_hash_aref(opts, sym_azure);
|
369
|
+
contained = rb_hash_aref(opts, sym_contained);
|
370
|
+
use_utf16 = rb_hash_aref(opts, sym_use_utf16);
|
371
|
+
cwrap->userdata->message_handler = rb_hash_aref(opts, sym_message_handler);
|
309
372
|
/* Dealing with options. */
|
310
373
|
if (dbinit() == FAIL) {
|
311
374
|
rb_raise(cTinyTdsError, "failed dbinit() function");
|
@@ -324,37 +387,52 @@ static VALUE rb_tinytds_connect(VALUE self, VALUE opts) {
|
|
324
387
|
dbsetlapp(cwrap->login, StringValueCStr(app));
|
325
388
|
if (!NIL_P(ltimeout))
|
326
389
|
dbsetlogintime(NUM2INT(ltimeout));
|
327
|
-
if (!NIL_P(timeout))
|
328
|
-
dbsettime(NUM2INT(timeout));
|
329
390
|
if (!NIL_P(charset))
|
330
391
|
DBSETLCHARSET(cwrap->login, StringValueCStr(charset));
|
331
|
-
if (!NIL_P(database)
|
332
|
-
|
333
|
-
DBSETLDBNAME
|
334
|
-
|
335
|
-
|
336
|
-
|
392
|
+
if (!NIL_P(database)) {
|
393
|
+
if (azure == Qtrue || contained == Qtrue) {
|
394
|
+
#ifdef DBSETLDBNAME
|
395
|
+
DBSETLDBNAME(cwrap->login, StringValueCStr(database));
|
396
|
+
#else
|
397
|
+
if (azure == Qtrue) {
|
398
|
+
rb_warn("TinyTds: :azure option is not supported in this version of FreeTDS.\n");
|
399
|
+
}
|
400
|
+
if (contained == Qtrue) {
|
401
|
+
rb_warn("TinyTds: :contained option is not supported in this version of FreeTDS.\n");
|
402
|
+
}
|
403
|
+
#endif
|
404
|
+
}
|
337
405
|
}
|
406
|
+
if (use_utf16 == Qtrue) { DBSETLUTF16(cwrap->login, 1); }
|
407
|
+
if (use_utf16 == Qfalse) { DBSETLUTF16(cwrap->login, 0); }
|
408
|
+
|
338
409
|
cwrap->client = dbopen(cwrap->login, StringValueCStr(dataserver));
|
339
410
|
if (cwrap->client) {
|
340
|
-
|
411
|
+
if (dbtds(cwrap->client) < 11) {
|
412
|
+
rb_raise(cTinyTdsError, "connecting with a TDS version older than 7.3!");
|
413
|
+
}
|
414
|
+
|
415
|
+
VALUE transposed_encoding, timeout_string;
|
341
416
|
|
342
417
|
cwrap->closed = 0;
|
343
418
|
cwrap->charset = charset;
|
344
419
|
if (!NIL_P(version))
|
345
420
|
dbsetversion(NUM2INT(version));
|
421
|
+
if (!NIL_P(timeout)) {
|
422
|
+
timeout_string = rb_sprintf("%"PRIsVALUE"", timeout);
|
423
|
+
if (dbsetopt(cwrap->client, DBSETTIME, StringValueCStr(timeout_string), 0) == FAIL) {
|
424
|
+
dbsettime(NUM2INT(timeout));
|
425
|
+
}
|
426
|
+
}
|
346
427
|
dbsetuserdata(cwrap->client, (BYTE*)cwrap->userdata);
|
428
|
+
dbsetinterrupt(cwrap->client, check_interrupt, handle_interrupt);
|
347
429
|
cwrap->userdata->closed = 0;
|
348
430
|
if (!NIL_P(database) && (azure != Qtrue)) {
|
349
431
|
dbuse(cwrap->client, StringValueCStr(database));
|
350
432
|
}
|
351
433
|
transposed_encoding = rb_funcall(cTinyTdsClient, intern_transpose_iconv_encoding, 1, charset);
|
352
434
|
cwrap->encoding = rb_enc_find(StringValueCStr(transposed_encoding));
|
353
|
-
|
354
|
-
cwrap->identity_insert_sql = "SELECT CAST(@@IDENTITY AS bigint) AS Ident";
|
355
|
-
} else {
|
356
|
-
cwrap->identity_insert_sql = "SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident";
|
357
|
-
}
|
435
|
+
cwrap->identity_insert_sql = "SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident";
|
358
436
|
}
|
359
437
|
return self;
|
360
438
|
}
|
@@ -391,6 +469,9 @@ void init_tinytds_client() {
|
|
391
469
|
sym_timeout = ID2SYM(rb_intern("timeout"));
|
392
470
|
sym_encoding = ID2SYM(rb_intern("encoding"));
|
393
471
|
sym_azure = ID2SYM(rb_intern("azure"));
|
472
|
+
sym_contained = ID2SYM(rb_intern("contained"));
|
473
|
+
sym_use_utf16 = ID2SYM(rb_intern("use_utf16"));
|
474
|
+
sym_message_handler = ID2SYM(rb_intern("message_handler"));
|
394
475
|
/* Intern TinyTds::Error Accessors */
|
395
476
|
intern_source_eql = rb_intern("source=");
|
396
477
|
intern_severity_eql = rb_intern("severity=");
|
@@ -402,6 +483,7 @@ void init_tinytds_client() {
|
|
402
483
|
intern_transpose_iconv_encoding = rb_intern("transpose_iconv_encoding");
|
403
484
|
intern_local_offset = rb_intern("local_offset");
|
404
485
|
intern_gsub = rb_intern("gsub");
|
486
|
+
intern_call = rb_intern("call");
|
405
487
|
/* Escape Regexp Global */
|
406
488
|
opt_escape_regex = rb_funcall(rb_cRegexp, intern_new, 1, rb_str_new2("\\\'"));
|
407
489
|
opt_escape_dblquote = rb_str_new2("''");
|
data/ext/tiny_tds/client.h
CHANGED
@@ -4,11 +4,14 @@
|
|
4
4
|
|
5
5
|
void init_tinytds_client();
|
6
6
|
|
7
|
+
#define ERROR_MSG_SIZE 1024
|
8
|
+
#define ERRORS_STACK_INIT_SIZE 2
|
9
|
+
|
7
10
|
typedef struct {
|
8
|
-
|
11
|
+
int is_message;
|
9
12
|
int cancel;
|
10
|
-
char error[
|
11
|
-
char source[
|
13
|
+
char error[ERROR_MSG_SIZE];
|
14
|
+
char source[ERROR_MSG_SIZE];
|
12
15
|
int severity;
|
13
16
|
int dberr;
|
14
17
|
int oserr;
|
@@ -22,7 +25,10 @@ typedef struct {
|
|
22
25
|
RETCODE dbsqlok_retcode;
|
23
26
|
short int dbcancel_sent;
|
24
27
|
short int nonblocking;
|
25
|
-
|
28
|
+
short int nonblocking_errors_length;
|
29
|
+
short int nonblocking_errors_size;
|
30
|
+
tinytds_errordata *nonblocking_errors;
|
31
|
+
VALUE message_handler;
|
26
32
|
} tinytds_client_userdata;
|
27
33
|
|
28
34
|
typedef struct {
|
@@ -36,7 +42,7 @@ typedef struct {
|
|
36
42
|
rb_encoding *encoding;
|
37
43
|
} tinytds_client_wrapper;
|
38
44
|
|
39
|
-
VALUE rb_tinytds_raise_error(DBPROCESS *dbproc,
|
45
|
+
VALUE rb_tinytds_raise_error(DBPROCESS *dbproc, tinytds_errordata error);
|
40
46
|
|
41
47
|
// Lib Macros
|
42
48
|
|