tiny_tds 2.1.6-x64-mingw-ucrt

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +407 -0
  3. data/.codeclimate.yml +20 -0
  4. data/.gitattributes +1 -0
  5. data/.gitignore +22 -0
  6. data/.rubocop.yml +31 -0
  7. data/CHANGELOG.md +280 -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 +504 -0
  13. data/Rakefile +62 -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 +499 -0
  20. data/ext/tiny_tds/client.h +53 -0
  21. data/ext/tiny_tds/extconf.rb +92 -0
  22. data/ext/tiny_tds/extconsts.rb +15 -0
  23. data/ext/tiny_tds/result.c +634 -0
  24. data/ext/tiny_tds/result.h +32 -0
  25. data/ext/tiny_tds/tiny_tds_ext.c +12 -0
  26. data/ext/tiny_tds/tiny_tds_ext.h +17 -0
  27. data/lib/tiny_tds/3.1/tiny_tds.so +0 -0
  28. data/lib/tiny_tds/3.2/tiny_tds.so +0 -0
  29. data/lib/tiny_tds/bin.rb +104 -0
  30. data/lib/tiny_tds/client.rb +136 -0
  31. data/lib/tiny_tds/error.rb +14 -0
  32. data/lib/tiny_tds/gem.rb +27 -0
  33. data/lib/tiny_tds/result.rb +7 -0
  34. data/lib/tiny_tds/version.rb +3 -0
  35. data/lib/tiny_tds.rb +61 -0
  36. data/patches/freetds/1.00.27/0001-mingw_missing_inet_pton.diff +34 -0
  37. data/patches/freetds/1.00.27/0002-Don-t-use-MSYS2-file-libws2_32.diff +28 -0
  38. data/patches/libiconv/1.14/1-avoid-gets-error.patch +17 -0
  39. data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/bsqldb.exe +0 -0
  40. data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/datacopy.exe +0 -0
  41. data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/defncopy.exe +0 -0
  42. data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/freebcp.exe +0 -0
  43. data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/libct-4.dll +0 -0
  44. data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/libsybdb-5.dll +0 -0
  45. data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/osql +388 -0
  46. data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/tdspool.exe +0 -0
  47. data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/tsql.exe +0 -0
  48. data/ports/x64-mingw-ucrt/freetds/1.1.24/lib/libct.dll.a +0 -0
  49. data/ports/x64-mingw-ucrt/freetds/1.1.24/lib/libct.la +41 -0
  50. data/ports/x64-mingw-ucrt/freetds/1.1.24/lib/libsybdb.dll.a +0 -0
  51. data/ports/x64-mingw-ucrt/freetds/1.1.24/lib/libsybdb.la +41 -0
  52. data/ports/x64-mingw-ucrt/libiconv/1.15/bin/iconv.exe +0 -0
  53. data/ports/x64-mingw-ucrt/libiconv/1.15/bin/libcharset-1.dll +0 -0
  54. data/ports/x64-mingw-ucrt/libiconv/1.15/bin/libiconv-2.dll +0 -0
  55. data/ports/x64-mingw-ucrt/libiconv/1.15/lib/charset.alias +4 -0
  56. data/ports/x64-mingw-ucrt/libiconv/1.15/lib/libcharset.dll.a +0 -0
  57. data/ports/x64-mingw-ucrt/libiconv/1.15/lib/libcharset.la +41 -0
  58. data/ports/x64-mingw-ucrt/libiconv/1.15/lib/libiconv.dll.a +0 -0
  59. data/ports/x64-mingw-ucrt/libiconv/1.15/lib/libiconv.la +41 -0
  60. data/ports/x64-mingw-ucrt/openssl/1.1.1s/bin/c_rehash +251 -0
  61. data/ports/x64-mingw-ucrt/openssl/1.1.1s/bin/libcrypto-1_1-x64.dll +0 -0
  62. data/ports/x64-mingw-ucrt/openssl/1.1.1s/bin/libssl-1_1-x64.dll +0 -0
  63. data/ports/x64-mingw-ucrt/openssl/1.1.1s/bin/openssl.exe +0 -0
  64. data/ports/x64-mingw-ucrt/openssl/1.1.1s/lib/libcrypto.a +0 -0
  65. data/ports/x64-mingw-ucrt/openssl/1.1.1s/lib/libcrypto.dll.a +0 -0
  66. data/ports/x64-mingw-ucrt/openssl/1.1.1s/lib/libssl.a +0 -0
  67. data/ports/x64-mingw-ucrt/openssl/1.1.1s/lib/libssl.dll.a +0 -0
  68. data/setup_cimgruby_dev.sh +25 -0
  69. data/start_dev.sh +21 -0
  70. data/tasks/native_gem.rake +23 -0
  71. data/tasks/package.rake +8 -0
  72. data/tasks/ports/freetds.rb +37 -0
  73. data/tasks/ports/libiconv.rb +26 -0
  74. data/tasks/ports/openssl.rb +62 -0
  75. data/tasks/ports/recipe.rb +64 -0
  76. data/tasks/ports.rake +108 -0
  77. data/tasks/test.rake +9 -0
  78. data/test/benchmark/query.rb +77 -0
  79. data/test/benchmark/query_odbc.rb +106 -0
  80. data/test/benchmark/query_tinytds.rb +126 -0
  81. data/test/bin/install-freetds.sh +20 -0
  82. data/test/bin/install-mssql.ps1 +31 -0
  83. data/test/bin/install-mssqltools.sh +9 -0
  84. data/test/bin/install-openssl.sh +18 -0
  85. data/test/bin/setup_tinytds_db.sh +7 -0
  86. data/test/bin/setup_volume_permissions.sh +10 -0
  87. data/test/client_test.rb +275 -0
  88. data/test/gem_test.rb +177 -0
  89. data/test/result_test.rb +814 -0
  90. data/test/schema/1px.gif +0 -0
  91. data/test/schema/sqlserver_2000.sql +140 -0
  92. data/test/schema/sqlserver_2005.sql +140 -0
  93. data/test/schema/sqlserver_2008.sql +140 -0
  94. data/test/schema/sqlserver_2014.sql +140 -0
  95. data/test/schema/sqlserver_2016.sql +140 -0
  96. data/test/schema/sqlserver_azure.sql +140 -0
  97. data/test/schema/sybase_ase.sql +138 -0
  98. data/test/schema_test.rb +443 -0
  99. data/test/sql/db-create.sql +18 -0
  100. data/test/sql/db-login.sql +38 -0
  101. data/test/test_helper.rb +280 -0
  102. data/test/thread_test.rb +98 -0
  103. data/tiny_tds.gemspec +31 -0
  104. metadata +267 -0
@@ -0,0 +1,499 @@
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_warn("TinyTds: dbsqlsend() returned FAIL.\n");
303
+ return Qfalse;
304
+ }
305
+ cwrap->userdata->dbsql_sent = 1;
306
+ result = rb_tinytds_new_result_obj(cwrap);
307
+ rb_iv_set(result, "@query_options", rb_funcall(rb_iv_get(self, "@query_options"), intern_dup, 0));
308
+ {
309
+ GET_RESULT_WRAPPER(result);
310
+ rwrap->local_offset = rb_funcall(cTinyTdsClient, intern_local_offset, 0);
311
+ rwrap->encoding = cwrap->encoding;
312
+ return result;
313
+ }
314
+ }
315
+
316
+ static VALUE rb_tinytds_charset(VALUE self) {
317
+ GET_CLIENT_WRAPPER(self);
318
+ return cwrap->charset;
319
+ }
320
+
321
+ static VALUE rb_tinytds_encoding(VALUE self) {
322
+ GET_CLIENT_WRAPPER(self);
323
+ return rb_enc_from_encoding(cwrap->encoding);
324
+ }
325
+
326
+ static VALUE rb_tinytds_escape(VALUE self, VALUE string) {
327
+ VALUE new_string;
328
+ GET_CLIENT_WRAPPER(self);
329
+
330
+ Check_Type(string, T_STRING);
331
+ new_string = rb_funcall(string, intern_gsub, 2, opt_escape_regex, opt_escape_dblquote);
332
+ rb_enc_associate(new_string, cwrap->encoding);
333
+ return new_string;
334
+ }
335
+
336
+ /* Duplicated in result.c */
337
+ static VALUE rb_tinytds_return_code(VALUE self) {
338
+ GET_CLIENT_WRAPPER(self);
339
+ if (cwrap->client && dbhasretstat(cwrap->client)) {
340
+ return LONG2NUM((long)dbretstatus(cwrap->client));
341
+ } else {
342
+ return Qnil;
343
+ }
344
+ }
345
+
346
+ static VALUE rb_tinytds_identity_sql(VALUE self) {
347
+ GET_CLIENT_WRAPPER(self);
348
+ return rb_str_new2(cwrap->identity_insert_sql);
349
+ }
350
+
351
+
352
+
353
+ // TinyTds::Client (protected)
354
+
355
+ static VALUE rb_tinytds_connect(VALUE self, VALUE opts) {
356
+ /* Parsing options hash to local vars. */
357
+ VALUE user, pass, dataserver, database, app, version, ltimeout, timeout, charset, azure, contained, use_utf16;
358
+ GET_CLIENT_WRAPPER(self);
359
+
360
+ user = rb_hash_aref(opts, sym_username);
361
+ pass = rb_hash_aref(opts, sym_password);
362
+ dataserver = rb_hash_aref(opts, sym_dataserver);
363
+ database = rb_hash_aref(opts, sym_database);
364
+ app = rb_hash_aref(opts, sym_appname);
365
+ version = rb_hash_aref(opts, sym_tds_version);
366
+ ltimeout = rb_hash_aref(opts, sym_login_timeout);
367
+ timeout = rb_hash_aref(opts, sym_timeout);
368
+ charset = rb_hash_aref(opts, sym_encoding);
369
+ azure = rb_hash_aref(opts, sym_azure);
370
+ contained = rb_hash_aref(opts, sym_contained);
371
+ use_utf16 = rb_hash_aref(opts, sym_use_utf16);
372
+ cwrap->userdata->message_handler = rb_hash_aref(opts, sym_message_handler);
373
+ /* Dealing with options. */
374
+ if (dbinit() == FAIL) {
375
+ rb_raise(cTinyTdsError, "failed dbinit() function");
376
+ return self;
377
+ }
378
+ dberrhandle(tinytds_err_handler);
379
+ dbmsghandle(tinytds_msg_handler);
380
+ cwrap->login = dblogin();
381
+ if (!NIL_P(version))
382
+ dbsetlversion(cwrap->login, NUM2INT(version));
383
+ if (!NIL_P(user))
384
+ dbsetluser(cwrap->login, StringValueCStr(user));
385
+ if (!NIL_P(pass))
386
+ dbsetlpwd(cwrap->login, StringValueCStr(pass));
387
+ if (!NIL_P(app))
388
+ dbsetlapp(cwrap->login, StringValueCStr(app));
389
+ if (!NIL_P(ltimeout))
390
+ dbsetlogintime(NUM2INT(ltimeout));
391
+ if (!NIL_P(charset))
392
+ DBSETLCHARSET(cwrap->login, StringValueCStr(charset));
393
+ if (!NIL_P(database)) {
394
+ if (azure == Qtrue || contained == Qtrue) {
395
+ #ifdef DBSETLDBNAME
396
+ DBSETLDBNAME(cwrap->login, StringValueCStr(database));
397
+ #else
398
+ if (azure == Qtrue) {
399
+ rb_warn("TinyTds: :azure option is not supported in this version of FreeTDS.\n");
400
+ }
401
+ if (contained == Qtrue) {
402
+ rb_warn("TinyTds: :contained option is not supported in this version of FreeTDS.\n");
403
+ }
404
+ #endif
405
+ }
406
+ }
407
+ #ifdef DBSETUTF16
408
+ if (use_utf16 == Qtrue) { DBSETLUTF16(cwrap->login, 1); }
409
+ if (use_utf16 == Qfalse) { DBSETLUTF16(cwrap->login, 0); }
410
+ #else
411
+ if (use_utf16 == Qtrue || use_utf16 == Qfalse) {
412
+ rb_warning("TinyTds: Please consider upgrading to FreeTDS 0.99 or higher for better unicode support.\n");
413
+ }
414
+ #endif
415
+
416
+ cwrap->client = dbopen(cwrap->login, StringValueCStr(dataserver));
417
+ if (cwrap->client) {
418
+ VALUE transposed_encoding, timeout_string;
419
+
420
+ cwrap->closed = 0;
421
+ cwrap->charset = charset;
422
+ if (!NIL_P(version))
423
+ dbsetversion(NUM2INT(version));
424
+ if (!NIL_P(timeout)) {
425
+ timeout_string = rb_sprintf("%"PRIsVALUE"", timeout);
426
+ if (dbsetopt(cwrap->client, DBSETTIME, StringValueCStr(timeout_string), 0) == FAIL) {
427
+ dbsettime(NUM2INT(timeout));
428
+ }
429
+ }
430
+ dbsetuserdata(cwrap->client, (BYTE*)cwrap->userdata);
431
+ dbsetinterrupt(cwrap->client, check_interrupt, handle_interrupt);
432
+ cwrap->userdata->closed = 0;
433
+ if (!NIL_P(database) && (azure != Qtrue)) {
434
+ dbuse(cwrap->client, StringValueCStr(database));
435
+ }
436
+ transposed_encoding = rb_funcall(cTinyTdsClient, intern_transpose_iconv_encoding, 1, charset);
437
+ cwrap->encoding = rb_enc_find(StringValueCStr(transposed_encoding));
438
+ if (dbtds(cwrap->client) <= 7) {
439
+ cwrap->identity_insert_sql = "SELECT CAST(@@IDENTITY AS bigint) AS Ident";
440
+ } else {
441
+ cwrap->identity_insert_sql = "SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident";
442
+ }
443
+ }
444
+ return self;
445
+ }
446
+
447
+
448
+ // Lib Init
449
+
450
+ void init_tinytds_client() {
451
+ cTinyTdsClient = rb_define_class_under(mTinyTds, "Client", rb_cObject);
452
+ rb_define_alloc_func(cTinyTdsClient, allocate);
453
+ /* Define TinyTds::Client Public Methods */
454
+ rb_define_method(cTinyTdsClient, "tds_version", rb_tinytds_tds_version, 0);
455
+ rb_define_method(cTinyTdsClient, "close", rb_tinytds_close, 0);
456
+ rb_define_method(cTinyTdsClient, "closed?", rb_tinytds_closed, 0);
457
+ rb_define_method(cTinyTdsClient, "canceled?", rb_tinytds_canceled, 0);
458
+ rb_define_method(cTinyTdsClient, "dead?", rb_tinytds_dead, 0);
459
+ rb_define_method(cTinyTdsClient, "sqlsent?", rb_tinytds_sqlsent, 0);
460
+ rb_define_method(cTinyTdsClient, "execute", rb_tinytds_execute, 1);
461
+ rb_define_method(cTinyTdsClient, "charset", rb_tinytds_charset, 0);
462
+ rb_define_method(cTinyTdsClient, "encoding", rb_tinytds_encoding, 0);
463
+ rb_define_method(cTinyTdsClient, "escape", rb_tinytds_escape, 1);
464
+ rb_define_method(cTinyTdsClient, "return_code", rb_tinytds_return_code, 0);
465
+ rb_define_method(cTinyTdsClient, "identity_sql", rb_tinytds_identity_sql, 0);
466
+ /* Define TinyTds::Client Protected Methods */
467
+ rb_define_protected_method(cTinyTdsClient, "connect", rb_tinytds_connect, 1);
468
+ /* Symbols For Connect */
469
+ sym_username = ID2SYM(rb_intern("username"));
470
+ sym_password = ID2SYM(rb_intern("password"));
471
+ sym_dataserver = ID2SYM(rb_intern("dataserver"));
472
+ sym_database = ID2SYM(rb_intern("database"));
473
+ sym_appname = ID2SYM(rb_intern("appname"));
474
+ sym_tds_version = ID2SYM(rb_intern("tds_version"));
475
+ sym_login_timeout = ID2SYM(rb_intern("login_timeout"));
476
+ sym_timeout = ID2SYM(rb_intern("timeout"));
477
+ sym_encoding = ID2SYM(rb_intern("encoding"));
478
+ sym_azure = ID2SYM(rb_intern("azure"));
479
+ sym_contained = ID2SYM(rb_intern("contained"));
480
+ sym_use_utf16 = ID2SYM(rb_intern("use_utf16"));
481
+ sym_message_handler = ID2SYM(rb_intern("message_handler"));
482
+ /* Intern TinyTds::Error Accessors */
483
+ intern_source_eql = rb_intern("source=");
484
+ intern_severity_eql = rb_intern("severity=");
485
+ intern_db_error_number_eql = rb_intern("db_error_number=");
486
+ intern_os_error_number_eql = rb_intern("os_error_number=");
487
+ /* Intern Misc */
488
+ intern_new = rb_intern("new");
489
+ intern_dup = rb_intern("dup");
490
+ intern_transpose_iconv_encoding = rb_intern("transpose_iconv_encoding");
491
+ intern_local_offset = rb_intern("local_offset");
492
+ intern_gsub = rb_intern("gsub");
493
+ intern_call = rb_intern("call");
494
+ /* Escape Regexp Global */
495
+ opt_escape_regex = rb_funcall(rb_cRegexp, intern_new, 1, rb_str_new2("\\\'"));
496
+ opt_escape_dblquote = rb_str_new2("''");
497
+ rb_global_variable(&opt_escape_regex);
498
+ rb_global_variable(&opt_escape_dblquote);
499
+ }
@@ -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
@@ -0,0 +1,92 @@
1
+ ENV['RC_ARCHS'] = '' if RUBY_PLATFORM =~ /darwin/
2
+
3
+ # :stopdoc:
4
+
5
+ require 'mkmf'
6
+ require 'rbconfig'
7
+ require_relative './extconsts'
8
+
9
+ # Shamelessly copied from nokogiri
10
+ #
11
+
12
+ def do_help
13
+ print <<HELP
14
+ usage: ruby #{$0} [options]
15
+ --with-freetds-dir=DIR
16
+ Use the freetds library placed under DIR.
17
+ HELP
18
+ exit! 0
19
+ end
20
+
21
+ do_help if arg_config('--help')
22
+
23
+ # Make sure to check the ports path for the configured host
24
+ architecture = RbConfig::CONFIG['arch']
25
+ architecture = "x86-mingw32" if architecture == "i386-mingw32"
26
+
27
+ project_dir = File.expand_path("../../..", __FILE__)
28
+ freetds_ports_dir = File.join(project_dir, 'ports', architecture, 'freetds', FREETDS_VERSION)
29
+ freetds_ports_dir = File.expand_path(freetds_ports_dir)
30
+
31
+ # Add all the special path searching from the original tiny_tds build
32
+ # order is important here! First in, first searched.
33
+ DIRS = %w(
34
+ /opt/local
35
+ /usr/local
36
+ )
37
+
38
+ if RbConfig::CONFIG['host_os'] =~ /darwin/i
39
+ # Ruby below 2.7 seems to label the host CPU on Apple Silicon as aarch64
40
+ # 2.7 and above print is as ARM64
41
+ target_host_cpu = Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.7') ? 'aarch64' : 'arm64'
42
+
43
+ if RbConfig::CONFIG['host_cpu'] == target_host_cpu
44
+ # Homebrew on Apple Silicon installs into /opt/hombrew
45
+ # https://docs.brew.sh/Installation
46
+ # On Intel Macs, it is /usr/local, so no changes necessary to DIRS
47
+ DIRS.unshift("/opt/homebrew")
48
+ end
49
+ end
50
+
51
+ if ENV["RI_DEVKIT"] && ENV["MINGW_PREFIX"] # RubyInstaller Support
52
+ DIRS.unshift(File.join(ENV["RI_DEVKIT"], ENV["MINGW_PREFIX"]))
53
+ end
54
+
55
+ # Add the ports directory if it exists for local developer builds
56
+ DIRS.unshift(freetds_ports_dir) if File.directory?(freetds_ports_dir)
57
+
58
+ # Grab freetds environment variable for use by people on services like
59
+ # Heroku who they can't easily use bundler config to set directories
60
+ DIRS.unshift(ENV['FREETDS_DIR']) if ENV.has_key?('FREETDS_DIR')
61
+
62
+ # Add the search paths for freetds configured above
63
+ ldirs = DIRS.flat_map do |path|
64
+ ldir = "#{path}/lib"
65
+ [ldir, "#{ldir}/freetds"]
66
+ end
67
+
68
+ idirs = DIRS.flat_map do |path|
69
+ idir = "#{path}/include"
70
+ [idir, "#{idir}/freetds"]
71
+ end
72
+
73
+ puts "looking for freetds headers in the following directories:\n#{idirs.map{|a| " - #{a}\n"}.join}"
74
+ puts "looking for freetds library in the following directories:\n#{ldirs.map{|a| " - #{a}\n"}.join}"
75
+ dir_config('freetds', idirs, ldirs)
76
+
77
+ have_dependencies = [
78
+ find_header('sybfront.h'),
79
+ find_header('sybdb.h'),
80
+ find_library('sybdb', 'tdsdbopen'),
81
+ find_library('sybdb', 'dbanydatecrack')
82
+ ].inject(true) do |memo, current|
83
+ memo && current
84
+ end
85
+
86
+ unless have_dependencies
87
+ abort 'Failed! Do you have FreeTDS 0.95.80 or higher installed?'
88
+ end
89
+
90
+ create_makefile('tiny_tds/tiny_tds')
91
+
92
+ # :startdoc:
@@ -0,0 +1,15 @@
1
+
2
+ ICONV_VERSION = ENV['TINYTDS_ICONV_VERSION'] || "1.15"
3
+ ICONV_SOURCE_URI = "http://ftp.gnu.org/pub/gnu/libiconv/libiconv-#{ICONV_VERSION}.tar.gz"
4
+
5
+ OPENSSL_VERSION = ENV['TINYTDS_OPENSSL_VERSION'] || '1.1.1s'
6
+ OPENSSL_SOURCE_URI = "https://www.openssl.org/source/openssl-#{OPENSSL_VERSION}.tar.gz"
7
+
8
+ FREETDS_VERSION = ENV['TINYTDS_FREETDS_VERSION'] || "1.1.24"
9
+ FREETDS_VERSION_INFO = Hash.new { |h,k|
10
+ h[k] = {files: "http://www.freetds.org/files/stable/freetds-#{k}.tar.bz2"}
11
+ }
12
+ FREETDS_VERSION_INFO['1.00'] = {files: 'http://www.freetds.org/files/stable/freetds-1.00.tar.bz2'}
13
+ FREETDS_VERSION_INFO['0.99'] = {files: 'http://www.freetds.org/files/current/freetds-dev.0.99.678.tar.gz'}
14
+ FREETDS_VERSION_INFO['0.95'] = {files: 'http://www.freetds.org/files/stable/freetds-0.95.92.tar.gz'}
15
+ FREETDS_SOURCE_URI = FREETDS_VERSION_INFO[FREETDS_VERSION][:files]