tiny_tds 3.2.0-x86_64-linux-musl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +20 -0
  3. data/.gitattributes +1 -0
  4. data/.github/workflows/ci.yml +590 -0
  5. data/.gitignore +22 -0
  6. data/.rubocop.yml +31 -0
  7. data/CHANGELOG.md +305 -0
  8. data/CODE_OF_CONDUCT.md +31 -0
  9. data/Gemfile +2 -0
  10. data/ISSUE_TEMPLATE.md +38 -0
  11. data/MIT-LICENSE +23 -0
  12. data/README.md +493 -0
  13. data/Rakefile +67 -0
  14. data/VERSION +1 -0
  15. data/bin/defncopy-ttds +3 -0
  16. data/bin/tsql-ttds +3 -0
  17. data/docker-compose.yml +34 -0
  18. data/exe/.keep +0 -0
  19. data/ext/tiny_tds/client.c +492 -0
  20. data/ext/tiny_tds/client.h +53 -0
  21. data/ext/tiny_tds/extconf.rb +190 -0
  22. data/ext/tiny_tds/extconsts.rb +8 -0
  23. data/ext/tiny_tds/result.c +626 -0
  24. data/ext/tiny_tds/result.h +32 -0
  25. data/ext/tiny_tds/tiny_tds_ext.c +15 -0
  26. data/ext/tiny_tds/tiny_tds_ext.h +17 -0
  27. data/lib/tiny_tds/2.7/tiny_tds.so +0 -0
  28. data/lib/tiny_tds/3.0/tiny_tds.so +0 -0
  29. data/lib/tiny_tds/3.1/tiny_tds.so +0 -0
  30. data/lib/tiny_tds/3.2/tiny_tds.so +0 -0
  31. data/lib/tiny_tds/3.3/tiny_tds.so +0 -0
  32. data/lib/tiny_tds/3.4/tiny_tds.so +0 -0
  33. data/lib/tiny_tds/bin.rb +90 -0
  34. data/lib/tiny_tds/client.rb +132 -0
  35. data/lib/tiny_tds/error.rb +12 -0
  36. data/lib/tiny_tds/gem.rb +23 -0
  37. data/lib/tiny_tds/result.rb +5 -0
  38. data/lib/tiny_tds/version.rb +3 -0
  39. data/lib/tiny_tds.rb +42 -0
  40. data/patches/freetds/1.00.27/0001-mingw_missing_inet_pton.diff +34 -0
  41. data/patches/freetds/1.00.27/0002-Don-t-use-MSYS2-file-libws2_32.diff +28 -0
  42. data/patches/libiconv/1.14/1-avoid-gets-error.patch +17 -0
  43. data/ports/x86_64-linux-musl/bin/defncopy +0 -0
  44. data/ports/x86_64-linux-musl/bin/tsql +0 -0
  45. data/ports/x86_64-linux-musl/lib/libsybdb.so.5 +0 -0
  46. data/setup_cimgruby_dev.sh +25 -0
  47. data/start_dev.sh +21 -0
  48. data/tasks/native_gem.rake +16 -0
  49. data/tasks/package.rake +6 -0
  50. data/tasks/ports.rake +24 -0
  51. data/tasks/test.rake +7 -0
  52. data/test/bin/install-freetds.sh +18 -0
  53. data/test/bin/install-mssql.ps1 +42 -0
  54. data/test/bin/install-mssqltools.sh +9 -0
  55. data/test/bin/install-openssl.sh +18 -0
  56. data/test/bin/restore-from-native-gem.ps1 +10 -0
  57. data/test/bin/setup_tinytds_db.sh +7 -0
  58. data/test/bin/setup_volume_permissions.sh +10 -0
  59. data/test/client_test.rb +266 -0
  60. data/test/gem_test.rb +100 -0
  61. data/test/result_test.rb +708 -0
  62. data/test/schema/1px.gif +0 -0
  63. data/test/schema/sqlserver_2017.sql +140 -0
  64. data/test/schema/sqlserver_azure.sql +140 -0
  65. data/test/schema_test.rb +417 -0
  66. data/test/sql/db-create.sql +18 -0
  67. data/test/sql/db-login.sql +38 -0
  68. data/test/test_helper.rb +244 -0
  69. data/test/thread_test.rb +89 -0
  70. data/tiny_tds.gemspec +31 -0
  71. metadata +259 -0
@@ -0,0 +1,492 @@
1
+ #include <tiny_tds_ext.h>
2
+ #include <errno.h>
3
+
4
+ VALUE cTinyTdsClient;
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, sym_contained, sym_use_utf16, sym_message_handler;
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, intern_call;
9
+ VALUE opt_escape_regex, opt_escape_dblquote;
10
+
11
+
12
+ // Lib Macros
13
+
14
+ #define GET_CLIENT_WRAPPER(self) \
15
+ tinytds_client_wrapper *cwrap; \
16
+ Data_Get_Struct(self, tinytds_client_wrapper, cwrap)
17
+
18
+ #define REQUIRE_OPEN_CLIENT(cwrap) \
19
+ if (cwrap->closed || cwrap->userdata->closed) { \
20
+ rb_raise(cTinyTdsError, "closed connection"); \
21
+ return Qnil; \
22
+ }
23
+
24
+
25
+ // Lib Backend (Helpers)
26
+
27
+ VALUE rb_tinytds_raise_error(DBPROCESS *dbproc, tinytds_errordata error) {
28
+ VALUE e;
29
+ GET_CLIENT_USERDATA(dbproc);
30
+ if (error.cancel && !dbdead(dbproc) && userdata && !userdata->closed) {
31
+ userdata->dbsqlok_sent = 1;
32
+ dbsqlok(dbproc);
33
+ userdata->dbcancel_sent = 1;
34
+ dbcancel(dbproc);
35
+ }
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
+
54
+ rb_exc_raise(e);
55
+ return Qnil;
56
+ }
57
+
58
+
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
+ }
70
+
71
+ int tinytds_err_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr) {
72
+ static const char *source = "error";
73
+ /* Everything should cancel by default */
74
+ int return_value = INT_CANCEL;
75
+ int cancel = 0;
76
+
77
+ GET_CLIENT_USERDATA(dbproc);
78
+
79
+ /* These error codes are documented in include/sybdb.h in FreeTDS */
80
+ switch(dberr) {
81
+
82
+ /* We don't want to raise these as a ruby exception for various reasons */
83
+ case 100: /* SYBEVERDOWN, indicating the connection can only be v7.1 */
84
+ case SYBESEOF: /* Usually accompanied by another more useful error */
85
+ case SYBESMSG: /* Generic "check messages from server" error */
86
+ case SYBEICONVI: /* Just return ?s to the client, as explained in readme */
87
+ return return_value;
88
+
89
+ case SYBEICONVO:
90
+ dbfreebuf(dbproc);
91
+ return return_value;
92
+
93
+ case SYBETIME:
94
+ /*
95
+ SYBETIME is the only error that can send INT_TIMEOUT or INT_CONTINUE,
96
+ but we don't ever want to automatically retry. Instead have the app
97
+ decide what to do.
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
+ }
106
+ return_value = INT_TIMEOUT;
107
+ cancel = 1;
108
+ break;
109
+
110
+ case SYBEWRIT:
111
+ /* Write errors may happen after we abort a statement */
112
+ if (userdata && (userdata->dbsqlok_sent || userdata->dbcancel_sent)) {
113
+ return return_value;
114
+ }
115
+ cancel = 1;
116
+ break;
117
+ }
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
+
129
+ /*
130
+ When in non-blocking mode we need to store the exception data to throw it
131
+ once the blocking call returns, otherwise we will segfault ruby since part
132
+ of the contract of the ruby non-blocking indicator is that you do not call
133
+ any of the ruby C API.
134
+ */
135
+ if (userdata && userdata->nonblocking) {
136
+ if (cancel && !dbdead(dbproc) && !userdata->closed) {
137
+ dbcancel(dbproc);
138
+ userdata->dbcancel_sent = 1;
139
+ }
140
+ push_userdata_error(userdata, error_data);
141
+ } else {
142
+ rb_tinytds_raise_error(dbproc, error_data);
143
+ }
144
+
145
+ return return_value;
146
+ }
147
+
148
+ int tinytds_msg_handler(DBPROCESS *dbproc, DBINT msgno, int msgstate, int severity, char *msgtext, char *srvname, char *procname, int line) {
149
+ static const char *source = "message";
150
+ GET_CLIENT_USERDATA(dbproc);
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;
176
+ }
177
+ } else {
178
+ rb_tinytds_raise_error(dbproc, error_data);
179
+ }
180
+ return 0;
181
+ }
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
+
210
+ static void rb_tinytds_client_reset_userdata(tinytds_client_userdata *userdata) {
211
+ userdata->timing_out = 0;
212
+ userdata->dbsql_sent = 0;
213
+ userdata->dbsqlok_sent = 0;
214
+ userdata->dbcancel_sent = 0;
215
+ userdata->nonblocking = 0;
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;
220
+ }
221
+
222
+ static void rb_tinytds_client_mark(void *ptr) {
223
+ tinytds_client_wrapper *cwrap = (tinytds_client_wrapper *)ptr;
224
+ if (cwrap) {
225
+ rb_gc_mark(cwrap->charset);
226
+ }
227
+ }
228
+
229
+ static void rb_tinytds_client_free(void *ptr) {
230
+ tinytds_client_wrapper *cwrap = (tinytds_client_wrapper *)ptr;
231
+ if (cwrap->login)
232
+ dbloginfree(cwrap->login);
233
+ if (cwrap->client && !cwrap->closed) {
234
+ dbclose(cwrap->client);
235
+ cwrap->client = NULL;
236
+ cwrap->closed = 1;
237
+ cwrap->userdata->closed = 1;
238
+ }
239
+ xfree(cwrap->userdata);
240
+ xfree(ptr);
241
+ }
242
+
243
+ static VALUE allocate(VALUE klass) {
244
+ VALUE obj;
245
+ tinytds_client_wrapper *cwrap;
246
+ obj = Data_Make_Struct(klass, tinytds_client_wrapper, rb_tinytds_client_mark, rb_tinytds_client_free, cwrap);
247
+ cwrap->closed = 1;
248
+ cwrap->charset = Qnil;
249
+ cwrap->userdata = malloc(sizeof(tinytds_client_userdata));
250
+ cwrap->userdata->closed = 1;
251
+ rb_tinytds_client_reset_userdata(cwrap->userdata);
252
+ return obj;
253
+ }
254
+
255
+
256
+ // TinyTds::Client (public)
257
+
258
+ static VALUE rb_tinytds_tds_version(VALUE self) {
259
+ GET_CLIENT_WRAPPER(self);
260
+ return INT2FIX(dbtds(cwrap->client));
261
+ }
262
+
263
+ static VALUE rb_tinytds_close(VALUE self) {
264
+ GET_CLIENT_WRAPPER(self);
265
+ if (cwrap->client && !cwrap->closed) {
266
+ dbclose(cwrap->client);
267
+ cwrap->client = NULL;
268
+ cwrap->closed = 1;
269
+ cwrap->userdata->closed = 1;
270
+ }
271
+ return Qtrue;
272
+ }
273
+
274
+ static VALUE rb_tinytds_dead(VALUE self) {
275
+ GET_CLIENT_WRAPPER(self);
276
+ return dbdead(cwrap->client) ? Qtrue : Qfalse;
277
+ }
278
+
279
+ static VALUE rb_tinytds_closed(VALUE self) {
280
+ GET_CLIENT_WRAPPER(self);
281
+ return (cwrap->closed || cwrap->userdata->closed) ? Qtrue : Qfalse;
282
+ }
283
+
284
+ static VALUE rb_tinytds_canceled(VALUE self) {
285
+ GET_CLIENT_WRAPPER(self);
286
+ return cwrap->userdata->dbcancel_sent ? Qtrue : Qfalse;
287
+ }
288
+
289
+ static VALUE rb_tinytds_sqlsent(VALUE self) {
290
+ GET_CLIENT_WRAPPER(self);
291
+ return cwrap->userdata->dbsql_sent ? Qtrue : Qfalse;
292
+ }
293
+
294
+ static VALUE rb_tinytds_execute(VALUE self, VALUE sql) {
295
+ VALUE result;
296
+
297
+ GET_CLIENT_WRAPPER(self);
298
+ rb_tinytds_client_reset_userdata(cwrap->userdata);
299
+ REQUIRE_OPEN_CLIENT(cwrap);
300
+ dbcmd(cwrap->client, StringValueCStr(sql));
301
+ if (dbsqlsend(cwrap->client) == FAIL) {
302
+ rb_raise(cTinyTdsError, "failed dbsqlsend() function");
303
+ }
304
+ cwrap->userdata->dbsql_sent = 1;
305
+ result = rb_tinytds_new_result_obj(cwrap);
306
+ rb_iv_set(result, "@query_options", rb_funcall(rb_iv_get(self, "@query_options"), intern_dup, 0));
307
+ {
308
+ GET_RESULT_WRAPPER(result);
309
+ rwrap->local_offset = rb_funcall(cTinyTdsClient, intern_local_offset, 0);
310
+ rwrap->encoding = cwrap->encoding;
311
+ return result;
312
+ }
313
+ }
314
+
315
+ static VALUE rb_tinytds_charset(VALUE self) {
316
+ GET_CLIENT_WRAPPER(self);
317
+ return cwrap->charset;
318
+ }
319
+
320
+ static VALUE rb_tinytds_encoding(VALUE self) {
321
+ GET_CLIENT_WRAPPER(self);
322
+ return rb_enc_from_encoding(cwrap->encoding);
323
+ }
324
+
325
+ static VALUE rb_tinytds_escape(VALUE self, VALUE string) {
326
+ VALUE new_string;
327
+ GET_CLIENT_WRAPPER(self);
328
+
329
+ Check_Type(string, T_STRING);
330
+ new_string = rb_funcall(string, intern_gsub, 2, opt_escape_regex, opt_escape_dblquote);
331
+ rb_enc_associate(new_string, cwrap->encoding);
332
+ return new_string;
333
+ }
334
+
335
+ /* Duplicated in result.c */
336
+ static VALUE rb_tinytds_return_code(VALUE self) {
337
+ GET_CLIENT_WRAPPER(self);
338
+ if (cwrap->client && dbhasretstat(cwrap->client)) {
339
+ return LONG2NUM((long)dbretstatus(cwrap->client));
340
+ } else {
341
+ return Qnil;
342
+ }
343
+ }
344
+
345
+ static VALUE rb_tinytds_identity_sql(VALUE self) {
346
+ GET_CLIENT_WRAPPER(self);
347
+ return rb_str_new2(cwrap->identity_insert_sql);
348
+ }
349
+
350
+
351
+
352
+ // TinyTds::Client (protected)
353
+
354
+ static VALUE rb_tinytds_connect(VALUE self, VALUE opts) {
355
+ /* Parsing options hash to local vars. */
356
+ VALUE user, pass, dataserver, database, app, version, ltimeout, timeout, charset, azure, contained, use_utf16;
357
+ GET_CLIENT_WRAPPER(self);
358
+
359
+ user = rb_hash_aref(opts, sym_username);
360
+ pass = rb_hash_aref(opts, sym_password);
361
+ dataserver = rb_hash_aref(opts, sym_dataserver);
362
+ database = rb_hash_aref(opts, sym_database);
363
+ app = rb_hash_aref(opts, sym_appname);
364
+ version = rb_hash_aref(opts, sym_tds_version);
365
+ ltimeout = rb_hash_aref(opts, sym_login_timeout);
366
+ timeout = rb_hash_aref(opts, sym_timeout);
367
+ charset = rb_hash_aref(opts, sym_encoding);
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);
372
+ /* Dealing with options. */
373
+ if (dbinit() == FAIL) {
374
+ rb_raise(cTinyTdsError, "failed dbinit() function");
375
+ return self;
376
+ }
377
+ dberrhandle(tinytds_err_handler);
378
+ dbmsghandle(tinytds_msg_handler);
379
+ cwrap->login = dblogin();
380
+ if (!NIL_P(version))
381
+ dbsetlversion(cwrap->login, NUM2INT(version));
382
+ if (!NIL_P(user))
383
+ dbsetluser(cwrap->login, StringValueCStr(user));
384
+ if (!NIL_P(pass))
385
+ dbsetlpwd(cwrap->login, StringValueCStr(pass));
386
+ if (!NIL_P(app))
387
+ dbsetlapp(cwrap->login, StringValueCStr(app));
388
+ if (!NIL_P(ltimeout))
389
+ dbsetlogintime(NUM2INT(ltimeout));
390
+ if (!NIL_P(charset))
391
+ DBSETLCHARSET(cwrap->login, StringValueCStr(charset));
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
+ }
405
+ }
406
+ if (use_utf16 == Qtrue) { DBSETLUTF16(cwrap->login, 1); }
407
+ if (use_utf16 == Qfalse) { DBSETLUTF16(cwrap->login, 0); }
408
+
409
+ cwrap->client = dbopen(cwrap->login, StringValueCStr(dataserver));
410
+ if (cwrap->client) {
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;
416
+
417
+ cwrap->closed = 0;
418
+ cwrap->charset = charset;
419
+ if (!NIL_P(version))
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
+ }
427
+ dbsetuserdata(cwrap->client, (BYTE*)cwrap->userdata);
428
+ dbsetinterrupt(cwrap->client, check_interrupt, handle_interrupt);
429
+ cwrap->userdata->closed = 0;
430
+ if (!NIL_P(database) && (azure != Qtrue)) {
431
+ dbuse(cwrap->client, StringValueCStr(database));
432
+ }
433
+ transposed_encoding = rb_funcall(cTinyTdsClient, intern_transpose_iconv_encoding, 1, charset);
434
+ cwrap->encoding = rb_enc_find(StringValueCStr(transposed_encoding));
435
+ cwrap->identity_insert_sql = "SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident";
436
+ }
437
+ return self;
438
+ }
439
+
440
+
441
+ // Lib Init
442
+
443
+ void init_tinytds_client() {
444
+ cTinyTdsClient = rb_define_class_under(mTinyTds, "Client", rb_cObject);
445
+ rb_define_alloc_func(cTinyTdsClient, allocate);
446
+ /* Define TinyTds::Client Public Methods */
447
+ rb_define_method(cTinyTdsClient, "tds_version", rb_tinytds_tds_version, 0);
448
+ rb_define_method(cTinyTdsClient, "close", rb_tinytds_close, 0);
449
+ rb_define_method(cTinyTdsClient, "closed?", rb_tinytds_closed, 0);
450
+ rb_define_method(cTinyTdsClient, "canceled?", rb_tinytds_canceled, 0);
451
+ rb_define_method(cTinyTdsClient, "dead?", rb_tinytds_dead, 0);
452
+ rb_define_method(cTinyTdsClient, "sqlsent?", rb_tinytds_sqlsent, 0);
453
+ rb_define_method(cTinyTdsClient, "execute", rb_tinytds_execute, 1);
454
+ rb_define_method(cTinyTdsClient, "charset", rb_tinytds_charset, 0);
455
+ rb_define_method(cTinyTdsClient, "encoding", rb_tinytds_encoding, 0);
456
+ rb_define_method(cTinyTdsClient, "escape", rb_tinytds_escape, 1);
457
+ rb_define_method(cTinyTdsClient, "return_code", rb_tinytds_return_code, 0);
458
+ rb_define_method(cTinyTdsClient, "identity_sql", rb_tinytds_identity_sql, 0);
459
+ /* Define TinyTds::Client Protected Methods */
460
+ rb_define_protected_method(cTinyTdsClient, "connect", rb_tinytds_connect, 1);
461
+ /* Symbols For Connect */
462
+ sym_username = ID2SYM(rb_intern("username"));
463
+ sym_password = ID2SYM(rb_intern("password"));
464
+ sym_dataserver = ID2SYM(rb_intern("dataserver"));
465
+ sym_database = ID2SYM(rb_intern("database"));
466
+ sym_appname = ID2SYM(rb_intern("appname"));
467
+ sym_tds_version = ID2SYM(rb_intern("tds_version"));
468
+ sym_login_timeout = ID2SYM(rb_intern("login_timeout"));
469
+ sym_timeout = ID2SYM(rb_intern("timeout"));
470
+ sym_encoding = ID2SYM(rb_intern("encoding"));
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"));
475
+ /* Intern TinyTds::Error Accessors */
476
+ intern_source_eql = rb_intern("source=");
477
+ intern_severity_eql = rb_intern("severity=");
478
+ intern_db_error_number_eql = rb_intern("db_error_number=");
479
+ intern_os_error_number_eql = rb_intern("os_error_number=");
480
+ /* Intern Misc */
481
+ intern_new = rb_intern("new");
482
+ intern_dup = rb_intern("dup");
483
+ intern_transpose_iconv_encoding = rb_intern("transpose_iconv_encoding");
484
+ intern_local_offset = rb_intern("local_offset");
485
+ intern_gsub = rb_intern("gsub");
486
+ intern_call = rb_intern("call");
487
+ /* Escape Regexp Global */
488
+ opt_escape_regex = rb_funcall(rb_cRegexp, intern_new, 1, rb_str_new2("\\\'"));
489
+ opt_escape_dblquote = rb_str_new2("''");
490
+ rb_global_variable(&opt_escape_regex);
491
+ rb_global_variable(&opt_escape_dblquote);
492
+ }
@@ -0,0 +1,53 @@
1
+
2
+ #ifndef TINYTDS_CLIENT_H
3
+ #define TINYTDS_CLIENT_H
4
+
5
+ void init_tinytds_client();
6
+
7
+ #define ERROR_MSG_SIZE 1024
8
+ #define ERRORS_STACK_INIT_SIZE 2
9
+
10
+ typedef struct {
11
+ int is_message;
12
+ int cancel;
13
+ char error[ERROR_MSG_SIZE];
14
+ char source[ERROR_MSG_SIZE];
15
+ int severity;
16
+ int dberr;
17
+ int oserr;
18
+ } tinytds_errordata;
19
+
20
+ typedef struct {
21
+ short int closed;
22
+ short int timing_out;
23
+ short int dbsql_sent;
24
+ short int dbsqlok_sent;
25
+ RETCODE dbsqlok_retcode;
26
+ short int dbcancel_sent;
27
+ short int nonblocking;
28
+ short int nonblocking_errors_length;
29
+ short int nonblocking_errors_size;
30
+ tinytds_errordata *nonblocking_errors;
31
+ VALUE message_handler;
32
+ } tinytds_client_userdata;
33
+
34
+ typedef struct {
35
+ LOGINREC *login;
36
+ RETCODE return_code;
37
+ DBPROCESS *client;
38
+ short int closed;
39
+ VALUE charset;
40
+ tinytds_client_userdata *userdata;
41
+ const char *identity_insert_sql;
42
+ rb_encoding *encoding;
43
+ } tinytds_client_wrapper;
44
+
45
+ VALUE rb_tinytds_raise_error(DBPROCESS *dbproc, tinytds_errordata error);
46
+
47
+ // Lib Macros
48
+
49
+ #define GET_CLIENT_USERDATA(dbproc) \
50
+ tinytds_client_userdata *userdata = (tinytds_client_userdata *)dbgetuserdata(dbproc);
51
+
52
+
53
+ #endif