vm_tiny_tds 2.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +20 -0
  3. data/.gitattributes +1 -0
  4. data/.gitignore +20 -0
  5. data/.rubocop.yml +31 -0
  6. data/.travis.yml +24 -0
  7. data/BACKERS.md +32 -0
  8. data/CHANGELOG.md +255 -0
  9. data/CODE_OF_CONDUCT.md +31 -0
  10. data/Gemfile +9 -0
  11. data/ISSUE_TEMPLATE.md +38 -0
  12. data/MIT-LICENSE +23 -0
  13. data/README.md +504 -0
  14. data/Rakefile +53 -0
  15. data/VERSION +1 -0
  16. data/appveyor.yml +51 -0
  17. data/bin/defncopy-ttds +3 -0
  18. data/bin/tsql-ttds +3 -0
  19. data/exe/.keep +0 -0
  20. data/ext/tiny_tds/client.c +451 -0
  21. data/ext/tiny_tds/client.h +51 -0
  22. data/ext/tiny_tds/extconf.rb +69 -0
  23. data/ext/tiny_tds/extconsts.rb +15 -0
  24. data/ext/tiny_tds/result.c +619 -0
  25. data/ext/tiny_tds/result.h +32 -0
  26. data/ext/tiny_tds/tiny_tds_ext.c +12 -0
  27. data/ext/tiny_tds/tiny_tds_ext.h +17 -0
  28. data/lib/tiny_tds/bin.rb +104 -0
  29. data/lib/tiny_tds/client.rb +136 -0
  30. data/lib/tiny_tds/error.rb +14 -0
  31. data/lib/tiny_tds/gem.rb +32 -0
  32. data/lib/tiny_tds/result.rb +7 -0
  33. data/lib/tiny_tds/version.rb +3 -0
  34. data/lib/tiny_tds.rb +61 -0
  35. data/patches/freetds/1.00.27/0001-mingw_missing_inet_pton.diff +34 -0
  36. data/patches/freetds/1.00.27/0002-Don-t-use-MSYS2-file-libws2_32.diff +28 -0
  37. data/patches/libiconv/1.14/1-avoid-gets-error.patch +17 -0
  38. data/tasks/native_gem.rake +14 -0
  39. data/tasks/package.rake +8 -0
  40. data/tasks/ports/freetds.rb +37 -0
  41. data/tasks/ports/libiconv.rb +43 -0
  42. data/tasks/ports/openssl.rb +78 -0
  43. data/tasks/ports/recipe.rb +52 -0
  44. data/tasks/ports.rake +87 -0
  45. data/tasks/test.rake +9 -0
  46. data/test/appveyor/dbsetup.ps1 +27 -0
  47. data/test/appveyor/dbsetup.sql +9 -0
  48. data/test/benchmark/query.rb +77 -0
  49. data/test/benchmark/query_odbc.rb +106 -0
  50. data/test/benchmark/query_tinytds.rb +126 -0
  51. data/test/bin/install-freetds.sh +20 -0
  52. data/test/bin/install-openssl.sh +18 -0
  53. data/test/bin/setup.sh +19 -0
  54. data/test/client_test.rb +230 -0
  55. data/test/gem_test.rb +179 -0
  56. data/test/result_test.rb +773 -0
  57. data/test/schema/1px.gif +0 -0
  58. data/test/schema/sqlserver_2000.sql +140 -0
  59. data/test/schema/sqlserver_2005.sql +140 -0
  60. data/test/schema/sqlserver_2008.sql +140 -0
  61. data/test/schema/sqlserver_2014.sql +140 -0
  62. data/test/schema/sqlserver_2016.sql +140 -0
  63. data/test/schema/sqlserver_azure.sql +140 -0
  64. data/test/schema/sybase_ase.sql +138 -0
  65. data/test/schema_test.rb +443 -0
  66. data/test/test_helper.rb +217 -0
  67. data/test/thread_test.rb +98 -0
  68. data/tiny_tds.gemspec +29 -0
  69. metadata +225 -0
@@ -0,0 +1,451 @@
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, int is_message, int cancel, const char *error, const char *source, int severity, int dberr, int oserr) {
28
+ VALUE e;
29
+ GET_CLIENT_USERDATA(dbproc);
30
+ if (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);
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));
44
+
45
+ if (severity <= 10 && 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
+
61
+ int tinytds_err_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr) {
62
+ static const char *source = "error";
63
+ /* Everything should cancel by default */
64
+ int return_value = INT_CANCEL;
65
+ int cancel = 0;
66
+
67
+ GET_CLIENT_USERDATA(dbproc);
68
+
69
+ /* These error codes are documented in include/sybdb.h in FreeTDS */
70
+ switch(dberr) {
71
+
72
+ /* We don't want to raise these as a ruby exception for various reasons */
73
+ case 100: /* SYBEVERDOWN, indicating the connection can only be v7.1 */
74
+ case SYBESEOF: /* Usually accompanied by another more useful error */
75
+ case SYBESMSG: /* Generic "check messages from server" error */
76
+ case SYBEICONVI: /* Just return ?s to the client, as explained in readme */
77
+ return return_value;
78
+
79
+ case SYBEICONVO:
80
+ dbfreebuf(dbproc);
81
+ return return_value;
82
+
83
+ case SYBETIME:
84
+ /*
85
+ SYBETIME is the only error that can send INT_TIMEOUT or INT_CONTINUE,
86
+ but we don't ever want to automatically retry. Instead have the app
87
+ decide what to do.
88
+ */
89
+ return_value = INT_TIMEOUT;
90
+ cancel = 1;
91
+ break;
92
+
93
+ case SYBEWRIT:
94
+ /* Write errors may happen after we abort a statement */
95
+ if (userdata && (userdata->dbsqlok_sent || userdata->dbcancel_sent)) {
96
+ return return_value;
97
+ }
98
+ cancel = 1;
99
+ break;
100
+ }
101
+
102
+ /*
103
+ When in non-blocking mode we need to store the exception data to throw it
104
+ once the blocking call returns, otherwise we will segfault ruby since part
105
+ of the contract of the ruby non-blocking indicator is that you do not call
106
+ any of the ruby C API.
107
+ */
108
+ if (userdata && userdata->nonblocking) {
109
+ if (cancel && !dbdead(dbproc) && !userdata->closed) {
110
+ dbcancel(dbproc);
111
+ userdata->dbcancel_sent = 1;
112
+ }
113
+
114
+ /*
115
+ If we've already captured an error message, don't overwrite it. This is
116
+ here because FreeTDS sends a generic "General SQL Server error" message
117
+ that will overwrite the real message. This is not normally a problem
118
+ because a ruby exception is normally thrown and we bail before the
119
+ generic message can be sent.
120
+ */
121
+ if (!userdata->nonblocking_error.is_set) {
122
+ userdata->nonblocking_error.is_message = 0;
123
+ userdata->nonblocking_error.cancel = cancel;
124
+ strncpy(userdata->nonblocking_error.error, dberrstr, ERROR_MSG_SIZE);
125
+ strncpy(userdata->nonblocking_error.source, source, ERROR_MSG_SIZE);
126
+ userdata->nonblocking_error.severity = severity;
127
+ userdata->nonblocking_error.dberr = dberr;
128
+ userdata->nonblocking_error.oserr = oserr;
129
+ userdata->nonblocking_error.is_set = 1;
130
+ }
131
+
132
+ } else {
133
+ rb_tinytds_raise_error(dbproc, 0, cancel, dberrstr, source, severity, dberr, oserr);
134
+ }
135
+
136
+ return return_value;
137
+ }
138
+
139
+ int tinytds_msg_handler(DBPROCESS *dbproc, DBINT msgno, int msgstate, int severity, char *msgtext, char *srvname, char *procname, int line) {
140
+ static const char *source = "message";
141
+ GET_CLIENT_USERDATA(dbproc);
142
+
143
+ int is_message_an_error = severity > 10 ? 1 : 0;
144
+
145
+ // See tinytds_err_handler() for info about why we do this
146
+ if (userdata && userdata->nonblocking) {
147
+ if (!userdata->nonblocking_error.is_set) {
148
+ userdata->nonblocking_error.is_message = !is_message_an_error;
149
+ userdata->nonblocking_error.cancel = is_message_an_error;
150
+ strncpy(userdata->nonblocking_error.error, msgtext, ERROR_MSG_SIZE);
151
+ strncpy(userdata->nonblocking_error.source, source, ERROR_MSG_SIZE);
152
+ userdata->nonblocking_error.severity = severity;
153
+ userdata->nonblocking_error.dberr = msgno;
154
+ userdata->nonblocking_error.oserr = msgstate;
155
+ userdata->nonblocking_error.is_set = 1;
156
+ }
157
+
158
+ if (is_message_an_error && !dbdead(dbproc) && !userdata->closed) {
159
+ dbcancel(dbproc);
160
+ userdata->dbcancel_sent = 1;
161
+ }
162
+ } else {
163
+ rb_tinytds_raise_error(dbproc, !is_message_an_error, is_message_an_error, msgtext, source, severity, msgno, msgstate);
164
+ }
165
+ return 0;
166
+ }
167
+
168
+ static void rb_tinytds_client_reset_userdata(tinytds_client_userdata *userdata) {
169
+ userdata->timing_out = 0;
170
+ userdata->dbsql_sent = 0;
171
+ userdata->dbsqlok_sent = 0;
172
+ userdata->dbcancel_sent = 0;
173
+ userdata->nonblocking = 0;
174
+ userdata->nonblocking_error.is_set = 0;
175
+ }
176
+
177
+ static void rb_tinytds_client_mark(void *ptr) {
178
+ tinytds_client_wrapper *cwrap = (tinytds_client_wrapper *)ptr;
179
+ if (cwrap) {
180
+ rb_gc_mark(cwrap->charset);
181
+ }
182
+ }
183
+
184
+ static void rb_tinytds_client_free(void *ptr) {
185
+ tinytds_client_wrapper *cwrap = (tinytds_client_wrapper *)ptr;
186
+ if (cwrap->login)
187
+ dbloginfree(cwrap->login);
188
+ if (cwrap->client && !cwrap->closed) {
189
+ dbclose(cwrap->client);
190
+ cwrap->closed = 1;
191
+ cwrap->userdata->closed = 1;
192
+ }
193
+ xfree(cwrap->userdata);
194
+ xfree(ptr);
195
+ }
196
+
197
+ static VALUE allocate(VALUE klass) {
198
+ VALUE obj;
199
+ tinytds_client_wrapper *cwrap;
200
+ obj = Data_Make_Struct(klass, tinytds_client_wrapper, rb_tinytds_client_mark, rb_tinytds_client_free, cwrap);
201
+ cwrap->closed = 1;
202
+ cwrap->charset = Qnil;
203
+ cwrap->userdata = malloc(sizeof(tinytds_client_userdata));
204
+ cwrap->userdata->closed = 1;
205
+ rb_tinytds_client_reset_userdata(cwrap->userdata);
206
+ return obj;
207
+ }
208
+
209
+
210
+ // TinyTds::Client (public)
211
+
212
+ static VALUE rb_tinytds_tds_version(VALUE self) {
213
+ GET_CLIENT_WRAPPER(self);
214
+ return INT2FIX(dbtds(cwrap->client));
215
+ }
216
+
217
+ static VALUE rb_tinytds_close(VALUE self) {
218
+ GET_CLIENT_WRAPPER(self);
219
+ if (cwrap->client && !cwrap->closed) {
220
+ dbclose(cwrap->client);
221
+ cwrap->closed = 1;
222
+ cwrap->userdata->closed = 1;
223
+ }
224
+ return Qtrue;
225
+ }
226
+
227
+ static VALUE rb_tinytds_dead(VALUE self) {
228
+ GET_CLIENT_WRAPPER(self);
229
+ return dbdead(cwrap->client) ? Qtrue : Qfalse;
230
+ }
231
+
232
+ static VALUE rb_tinytds_closed(VALUE self) {
233
+ GET_CLIENT_WRAPPER(self);
234
+ return (cwrap->closed || cwrap->userdata->closed) ? Qtrue : Qfalse;
235
+ }
236
+
237
+ static VALUE rb_tinytds_canceled(VALUE self) {
238
+ GET_CLIENT_WRAPPER(self);
239
+ return cwrap->userdata->dbcancel_sent ? Qtrue : Qfalse;
240
+ }
241
+
242
+ static VALUE rb_tinytds_sqlsent(VALUE self) {
243
+ GET_CLIENT_WRAPPER(self);
244
+ return cwrap->userdata->dbsql_sent ? Qtrue : Qfalse;
245
+ }
246
+
247
+ static VALUE rb_tinytds_execute(VALUE self, VALUE sql) {
248
+ VALUE result;
249
+
250
+ GET_CLIENT_WRAPPER(self);
251
+ rb_tinytds_client_reset_userdata(cwrap->userdata);
252
+ REQUIRE_OPEN_CLIENT(cwrap);
253
+ dbcmd(cwrap->client, StringValueCStr(sql));
254
+ if (dbsqlsend(cwrap->client) == FAIL) {
255
+ rb_warn("TinyTds: dbsqlsend() returned FAIL.\n");
256
+ return Qfalse;
257
+ }
258
+ cwrap->userdata->dbsql_sent = 1;
259
+ result = rb_tinytds_new_result_obj(cwrap);
260
+ rb_iv_set(result, "@query_options", rb_funcall(rb_iv_get(self, "@query_options"), intern_dup, 0));
261
+ {
262
+ GET_RESULT_WRAPPER(result);
263
+ rwrap->local_offset = rb_funcall(cTinyTdsClient, intern_local_offset, 0);
264
+ rwrap->encoding = cwrap->encoding;
265
+ return result;
266
+ }
267
+ }
268
+
269
+ static VALUE rb_tinytds_charset(VALUE self) {
270
+ GET_CLIENT_WRAPPER(self);
271
+ return cwrap->charset;
272
+ }
273
+
274
+ static VALUE rb_tinytds_encoding(VALUE self) {
275
+ GET_CLIENT_WRAPPER(self);
276
+ return rb_enc_from_encoding(cwrap->encoding);
277
+ }
278
+
279
+ static VALUE rb_tinytds_escape(VALUE self, VALUE string) {
280
+ VALUE new_string;
281
+ GET_CLIENT_WRAPPER(self);
282
+
283
+ Check_Type(string, T_STRING);
284
+ new_string = rb_funcall(string, intern_gsub, 2, opt_escape_regex, opt_escape_dblquote);
285
+ rb_enc_associate(new_string, cwrap->encoding);
286
+ return new_string;
287
+ }
288
+
289
+ /* Duplicated in result.c */
290
+ static VALUE rb_tinytds_return_code(VALUE self) {
291
+ GET_CLIENT_WRAPPER(self);
292
+ if (cwrap->client && dbhasretstat(cwrap->client)) {
293
+ return LONG2NUM((long)dbretstatus(cwrap->client));
294
+ } else {
295
+ return Qnil;
296
+ }
297
+ }
298
+
299
+ static VALUE rb_tinytds_identity_sql(VALUE self) {
300
+ GET_CLIENT_WRAPPER(self);
301
+ return rb_str_new2(cwrap->identity_insert_sql);
302
+ }
303
+
304
+
305
+
306
+ // TinyTds::Client (protected)
307
+
308
+ static VALUE rb_tinytds_connect(VALUE self, VALUE opts) {
309
+ /* Parsing options hash to local vars. */
310
+ VALUE user, pass, dataserver, database, app, version, ltimeout, timeout, charset, azure, contained, use_utf16;
311
+ GET_CLIENT_WRAPPER(self);
312
+
313
+ user = rb_hash_aref(opts, sym_username);
314
+ pass = rb_hash_aref(opts, sym_password);
315
+ dataserver = rb_hash_aref(opts, sym_dataserver);
316
+ database = rb_hash_aref(opts, sym_database);
317
+ app = rb_hash_aref(opts, sym_appname);
318
+ version = rb_hash_aref(opts, sym_tds_version);
319
+ ltimeout = rb_hash_aref(opts, sym_login_timeout);
320
+ timeout = rb_hash_aref(opts, sym_timeout);
321
+ charset = rb_hash_aref(opts, sym_encoding);
322
+ azure = rb_hash_aref(opts, sym_azure);
323
+ contained = rb_hash_aref(opts, sym_contained);
324
+ use_utf16 = rb_hash_aref(opts, sym_use_utf16);
325
+ cwrap->userdata->message_handler = rb_hash_aref(opts, sym_message_handler);
326
+ /* Dealing with options. */
327
+ if (dbinit() == FAIL) {
328
+ rb_raise(cTinyTdsError, "failed dbinit() function");
329
+ return self;
330
+ }
331
+ dberrhandle(tinytds_err_handler);
332
+ dbmsghandle(tinytds_msg_handler);
333
+ cwrap->login = dblogin();
334
+ if (!NIL_P(version))
335
+ dbsetlversion(cwrap->login, NUM2INT(version));
336
+ if (!NIL_P(user))
337
+ dbsetluser(cwrap->login, StringValueCStr(user));
338
+ if (!NIL_P(pass))
339
+ dbsetlpwd(cwrap->login, StringValueCStr(pass));
340
+ if (!NIL_P(app))
341
+ dbsetlapp(cwrap->login, StringValueCStr(app));
342
+ if (!NIL_P(ltimeout))
343
+ dbsetlogintime(NUM2INT(ltimeout));
344
+ if (!NIL_P(charset))
345
+ DBSETLCHARSET(cwrap->login, StringValueCStr(charset));
346
+ if (!NIL_P(database)) {
347
+ if (azure == Qtrue || contained == Qtrue) {
348
+ #ifdef DBSETLDBNAME
349
+ DBSETLDBNAME(cwrap->login, StringValueCStr(database));
350
+ #else
351
+ if (azure == Qtrue) {
352
+ rb_warn("TinyTds: :azure option is not supported in this version of FreeTDS.\n");
353
+ }
354
+ if (contained == Qtrue) {
355
+ rb_warn("TinyTds: :contained option is not supported in this version of FreeTDS.\n");
356
+ }
357
+ #endif
358
+ }
359
+ }
360
+ #ifdef DBSETUTF16
361
+ if (use_utf16 == Qtrue) { DBSETLUTF16(cwrap->login, 1); }
362
+ if (use_utf16 == Qfalse) { DBSETLUTF16(cwrap->login, 0); }
363
+ #else
364
+ if (use_utf16 == Qtrue || use_utf16 == Qfalse) {
365
+ rb_warning("TinyTds: Please consider upgrading to FreeTDS 0.99 or higher for better unicode support.\n");
366
+ }
367
+ #endif
368
+
369
+ cwrap->client = dbopen(cwrap->login, StringValueCStr(dataserver));
370
+ if (cwrap->client) {
371
+ VALUE transposed_encoding, timeout_string;
372
+
373
+ cwrap->closed = 0;
374
+ cwrap->charset = charset;
375
+ if (!NIL_P(version))
376
+ dbsetversion(NUM2INT(version));
377
+ if (!NIL_P(timeout)) {
378
+ timeout_string = rb_sprintf("%"PRIsVALUE"", timeout);
379
+ if (dbsetopt(cwrap->client, DBSETTIME, StringValueCStr(timeout_string), 0) == FAIL) {
380
+ dbsettime(NUM2INT(timeout));
381
+ }
382
+ }
383
+ dbsetuserdata(cwrap->client, (BYTE*)cwrap->userdata);
384
+ cwrap->userdata->closed = 0;
385
+ if (!NIL_P(database) && (azure != Qtrue)) {
386
+ dbuse(cwrap->client, StringValueCStr(database));
387
+ }
388
+ transposed_encoding = rb_funcall(cTinyTdsClient, intern_transpose_iconv_encoding, 1, charset);
389
+ cwrap->encoding = rb_enc_find(StringValueCStr(transposed_encoding));
390
+ if (dbtds(cwrap->client) <= 7) {
391
+ cwrap->identity_insert_sql = "SELECT CAST(@@IDENTITY AS bigint) AS Ident";
392
+ } else {
393
+ cwrap->identity_insert_sql = "SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident";
394
+ }
395
+ }
396
+ return self;
397
+ }
398
+
399
+
400
+ // Lib Init
401
+
402
+ void init_tinytds_client() {
403
+ cTinyTdsClient = rb_define_class_under(mTinyTds, "Client", rb_cObject);
404
+ rb_define_alloc_func(cTinyTdsClient, allocate);
405
+ /* Define TinyTds::Client Public Methods */
406
+ rb_define_method(cTinyTdsClient, "tds_version", rb_tinytds_tds_version, 0);
407
+ rb_define_method(cTinyTdsClient, "close", rb_tinytds_close, 0);
408
+ rb_define_method(cTinyTdsClient, "closed?", rb_tinytds_closed, 0);
409
+ rb_define_method(cTinyTdsClient, "canceled?", rb_tinytds_canceled, 0);
410
+ rb_define_method(cTinyTdsClient, "dead?", rb_tinytds_dead, 0);
411
+ rb_define_method(cTinyTdsClient, "sqlsent?", rb_tinytds_sqlsent, 0);
412
+ rb_define_method(cTinyTdsClient, "execute", rb_tinytds_execute, 1);
413
+ rb_define_method(cTinyTdsClient, "charset", rb_tinytds_charset, 0);
414
+ rb_define_method(cTinyTdsClient, "encoding", rb_tinytds_encoding, 0);
415
+ rb_define_method(cTinyTdsClient, "escape", rb_tinytds_escape, 1);
416
+ rb_define_method(cTinyTdsClient, "return_code", rb_tinytds_return_code, 0);
417
+ rb_define_method(cTinyTdsClient, "identity_sql", rb_tinytds_identity_sql, 0);
418
+ /* Define TinyTds::Client Protected Methods */
419
+ rb_define_protected_method(cTinyTdsClient, "connect", rb_tinytds_connect, 1);
420
+ /* Symbols For Connect */
421
+ sym_username = ID2SYM(rb_intern("username"));
422
+ sym_password = ID2SYM(rb_intern("password"));
423
+ sym_dataserver = ID2SYM(rb_intern("dataserver"));
424
+ sym_database = ID2SYM(rb_intern("database"));
425
+ sym_appname = ID2SYM(rb_intern("appname"));
426
+ sym_tds_version = ID2SYM(rb_intern("tds_version"));
427
+ sym_login_timeout = ID2SYM(rb_intern("login_timeout"));
428
+ sym_timeout = ID2SYM(rb_intern("timeout"));
429
+ sym_encoding = ID2SYM(rb_intern("encoding"));
430
+ sym_azure = ID2SYM(rb_intern("azure"));
431
+ sym_contained = ID2SYM(rb_intern("contained"));
432
+ sym_use_utf16 = ID2SYM(rb_intern("use_utf16"));
433
+ sym_message_handler = ID2SYM(rb_intern("message_handler"));
434
+ /* Intern TinyTds::Error Accessors */
435
+ intern_source_eql = rb_intern("source=");
436
+ intern_severity_eql = rb_intern("severity=");
437
+ intern_db_error_number_eql = rb_intern("db_error_number=");
438
+ intern_os_error_number_eql = rb_intern("os_error_number=");
439
+ /* Intern Misc */
440
+ intern_new = rb_intern("new");
441
+ intern_dup = rb_intern("dup");
442
+ intern_transpose_iconv_encoding = rb_intern("transpose_iconv_encoding");
443
+ intern_local_offset = rb_intern("local_offset");
444
+ intern_gsub = rb_intern("gsub");
445
+ intern_call = rb_intern("call");
446
+ /* Escape Regexp Global */
447
+ opt_escape_regex = rb_funcall(rb_cRegexp, intern_new, 1, rb_str_new2("\\\'"));
448
+ opt_escape_dblquote = rb_str_new2("''");
449
+ rb_global_variable(&opt_escape_regex);
450
+ rb_global_variable(&opt_escape_dblquote);
451
+ }
@@ -0,0 +1,51 @@
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
+
9
+ typedef struct {
10
+ short int is_set;
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
+ tinytds_errordata nonblocking_error;
29
+ VALUE message_handler;
30
+ } tinytds_client_userdata;
31
+
32
+ typedef struct {
33
+ LOGINREC *login;
34
+ RETCODE return_code;
35
+ DBPROCESS *client;
36
+ short int closed;
37
+ VALUE charset;
38
+ tinytds_client_userdata *userdata;
39
+ const char *identity_insert_sql;
40
+ rb_encoding *encoding;
41
+ } tinytds_client_wrapper;
42
+
43
+ VALUE rb_tinytds_raise_error(DBPROCESS *dbproc, int is_message, int cancel, const char *error, const char *source, int severity, int dberr, int oserr);
44
+
45
+ // Lib Macros
46
+
47
+ #define GET_CLIENT_USERDATA(dbproc) \
48
+ tinytds_client_userdata *userdata = (tinytds_client_userdata *)dbgetuserdata(dbproc);
49
+
50
+
51
+ #endif
@@ -0,0 +1,69 @@
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
+ host = RbConfig::CONFIG['host']
25
+ project_dir = File.join(['..']*4)
26
+ freetds_ports_dir = File.join(project_dir, 'ports', host, 'freetds', FREETDS_VERSION)
27
+ freetds_ports_dir = File.expand_path(freetds_ports_dir)
28
+
29
+ # Add all the special path searching from the original tiny_tds build
30
+ # order is important here! First in, last searched.
31
+ DIRS = %w(
32
+ /usr/local
33
+ /opt/local
34
+ )
35
+
36
+ # Grab freetds environment variable for use by people on services like
37
+ # Heroku who they can't easily use bundler config to set directories
38
+ DIRS.push(ENV['FREETDS_DIR']) if ENV.has_key?('FREETDS_DIR')
39
+
40
+ # Add the ports directory if it exists for local developer builds
41
+ DIRS.push(freetds_ports_dir) if File.directory?(freetds_ports_dir)
42
+
43
+ # Add the search paths for freetds configured above
44
+ DIRS.each do |path|
45
+ idir = "#{path}/include"
46
+ ldir = "#{path}/lib"
47
+
48
+ dir_config('freetds',
49
+ [idir, "#{idir}/freetds"],
50
+ [ldir, "#{ldir}/freetds"]
51
+ )
52
+ end
53
+
54
+ have_dependencies = [
55
+ find_header('sybfront.h'),
56
+ find_header('sybdb.h'),
57
+ find_library('sybdb', 'tdsdbopen'),
58
+ find_library('sybdb', 'dbanydatecrack')
59
+ ].inject(true) do |memo, current|
60
+ memo && current
61
+ end
62
+
63
+ unless have_dependencies
64
+ abort 'Failed! Do you have FreeTDS 0.95.80 or higher installed?'
65
+ end
66
+
67
+ create_makefile('tiny_tds/tiny_tds')
68
+
69
+ # :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.0j'
6
+ OPENSSL_SOURCE_URI = "https://www.openssl.org/source/openssl-#{OPENSSL_VERSION}.tar.gz"
7
+
8
+ FREETDS_VERSION = ENV['TINYTDS_FREETDS_VERSION'] || "1.00.27"
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]