tiny_tds 2.1.6-x64-mingw-ucrt

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.
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]