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,626 @@
1
+
2
+ #include <tiny_tds_ext.h>
3
+ #include <stdint.h>
4
+
5
+ // File Types/Vars
6
+
7
+ VALUE cTinyTdsResult;
8
+ extern VALUE mTinyTds, cTinyTdsClient, cTinyTdsError;
9
+ VALUE cKernel, cDate;
10
+ VALUE opt_decimal_zero, opt_float_zero, opt_one, opt_zero, opt_four, opt_19hdr, opt_onek, opt_tenk, opt_onemil, opt_onebil;
11
+ static ID intern_new, intern_utc, intern_local, intern_localtime, intern_merge,
12
+ intern_civil, intern_new_offset, intern_plus, intern_divide, intern_bigd;
13
+ static ID sym_symbolize_keys, sym_as, sym_array, sym_cache_rows, sym_first, sym_timezone, sym_local, sym_utc, sym_empty_sets;
14
+
15
+
16
+ // Lib Macros
17
+
18
+ rb_encoding *binaryEncoding;
19
+ #define ENCODED_STR_NEW(_data, _len) ({ \
20
+ VALUE _val = rb_str_new((char *)_data, (long)_len); \
21
+ rb_enc_associate(_val, rwrap->encoding); \
22
+ _val; \
23
+ })
24
+ #define ENCODED_STR_NEW2(_data2) ({ \
25
+ VALUE _val = rb_str_new2((char *)_data2); \
26
+ rb_enc_associate(_val, rwrap->encoding); \
27
+ _val; \
28
+ })
29
+
30
+ #ifdef _WIN32
31
+ #define LONG_LONG_FORMAT "I64d"
32
+ #else
33
+ #define LONG_LONG_FORMAT "lld"
34
+ #endif
35
+
36
+
37
+ // Lib Backend (Memory Management)
38
+
39
+ static void rb_tinytds_result_mark(void *ptr) {
40
+ tinytds_result_wrapper *rwrap = (tinytds_result_wrapper *)ptr;
41
+ if (rwrap) {
42
+ rb_gc_mark(rwrap->local_offset);
43
+ rb_gc_mark(rwrap->fields);
44
+ rb_gc_mark(rwrap->fields_processed);
45
+ rb_gc_mark(rwrap->results);
46
+ rb_gc_mark(rwrap->dbresults_retcodes);
47
+ }
48
+ }
49
+
50
+ static void rb_tinytds_result_free(void *ptr) {
51
+ xfree(ptr);
52
+ }
53
+
54
+ VALUE rb_tinytds_new_result_obj(tinytds_client_wrapper *cwrap) {
55
+ VALUE obj;
56
+ tinytds_result_wrapper *rwrap;
57
+ obj = Data_Make_Struct(cTinyTdsResult, tinytds_result_wrapper, rb_tinytds_result_mark, rb_tinytds_result_free, rwrap);
58
+ rwrap->cwrap = cwrap;
59
+ rwrap->client = cwrap->client;
60
+ rwrap->local_offset = Qnil;
61
+ rwrap->fields = rb_ary_new();
62
+ rwrap->fields_processed = rb_ary_new();
63
+ rwrap->results = Qnil;
64
+ rwrap->dbresults_retcodes = rb_ary_new();
65
+ rwrap->number_of_results = 0;
66
+ rwrap->number_of_fields = 0;
67
+ rwrap->number_of_rows = 0;
68
+ rb_obj_call_init(obj, 0, NULL);
69
+ return obj;
70
+ }
71
+
72
+ // No GVL Helpers
73
+
74
+ #define NOGVL_DBCALL(_dbfunction, _client) ( \
75
+ (RETCODE)(intptr_t)rb_thread_call_without_gvl( \
76
+ (void *(*)(void *))_dbfunction, _client, \
77
+ (rb_unblock_function_t*)dbcancel_ubf, _client ) \
78
+ )
79
+
80
+ static void dbcancel_ubf(DBPROCESS *client) {
81
+ GET_CLIENT_USERDATA(client);
82
+ dbcancel(client);
83
+ userdata->dbcancel_sent = 1;
84
+ }
85
+
86
+ static void nogvl_setup(DBPROCESS *client) {
87
+ GET_CLIENT_USERDATA(client);
88
+ userdata->nonblocking = 1;
89
+ userdata->nonblocking_errors_length = 0;
90
+ userdata->nonblocking_errors = malloc(ERRORS_STACK_INIT_SIZE * sizeof(tinytds_errordata));
91
+ userdata->nonblocking_errors_size = ERRORS_STACK_INIT_SIZE;
92
+ }
93
+
94
+ static void nogvl_cleanup(DBPROCESS *client) {
95
+ GET_CLIENT_USERDATA(client);
96
+ userdata->nonblocking = 0;
97
+ userdata->timing_out = 0;
98
+ /*
99
+ Now that the blocking operation is done, we can finally throw any
100
+ exceptions based on errors from SQL Server.
101
+ */
102
+ short int i;
103
+ for (i = 0; i < userdata->nonblocking_errors_length; i++) {
104
+ tinytds_errordata error = userdata->nonblocking_errors[i];
105
+
106
+ // lookahead to drain any info messages ahead of raising error
107
+ if (!error.is_message) {
108
+ short int j;
109
+ for (j = i; j < userdata->nonblocking_errors_length; j++) {
110
+ tinytds_errordata msg_error = userdata->nonblocking_errors[j];
111
+ if (msg_error.is_message) {
112
+ rb_tinytds_raise_error(client, msg_error);
113
+ }
114
+ }
115
+ }
116
+
117
+ rb_tinytds_raise_error(client, error);
118
+ }
119
+
120
+ free(userdata->nonblocking_errors);
121
+ userdata->nonblocking_errors_length = 0;
122
+ userdata->nonblocking_errors_size = 0;
123
+ }
124
+
125
+ static RETCODE nogvl_dbsqlok(DBPROCESS *client) {
126
+ int retcode = FAIL;
127
+ GET_CLIENT_USERDATA(client);
128
+ nogvl_setup(client);
129
+ retcode = NOGVL_DBCALL(dbsqlok, client);
130
+ nogvl_cleanup(client);
131
+ userdata->dbsqlok_sent = 1;
132
+ return retcode;
133
+ }
134
+
135
+ static RETCODE nogvl_dbsqlexec(DBPROCESS *client) {
136
+ int retcode = FAIL;
137
+ nogvl_setup(client);
138
+ retcode = NOGVL_DBCALL(dbsqlexec, client);
139
+ nogvl_cleanup(client);
140
+ return retcode;
141
+ }
142
+
143
+ static RETCODE nogvl_dbresults(DBPROCESS *client) {
144
+ int retcode = FAIL;
145
+ nogvl_setup(client);
146
+ retcode = NOGVL_DBCALL(dbresults, client);
147
+ nogvl_cleanup(client);
148
+ return retcode;
149
+ }
150
+
151
+ static RETCODE nogvl_dbnextrow(DBPROCESS * client) {
152
+ int retcode = FAIL;
153
+ nogvl_setup(client);
154
+ retcode = NOGVL_DBCALL(dbnextrow, client);
155
+ nogvl_cleanup(client);
156
+ return retcode;
157
+ }
158
+
159
+ // Lib Backend (Helpers)
160
+
161
+ static RETCODE rb_tinytds_result_dbresults_retcode(VALUE self) {
162
+ VALUE ruby_rc;
163
+ RETCODE db_rc;
164
+ GET_RESULT_WRAPPER(self);
165
+ ruby_rc = rb_ary_entry(rwrap->dbresults_retcodes, rwrap->number_of_results);
166
+ if (NIL_P(ruby_rc)) {
167
+ db_rc = nogvl_dbresults(rwrap->client);
168
+ ruby_rc = INT2FIX(db_rc);
169
+ rb_ary_store(rwrap->dbresults_retcodes, rwrap->number_of_results, ruby_rc);
170
+ } else {
171
+ db_rc = FIX2INT(ruby_rc);
172
+ }
173
+ return db_rc;
174
+ }
175
+
176
+ static RETCODE rb_tinytds_result_ok_helper(DBPROCESS *client) {
177
+ GET_CLIENT_USERDATA(client);
178
+ if (userdata->dbsqlok_sent == 0) {
179
+ userdata->dbsqlok_retcode = nogvl_dbsqlok(client);
180
+ }
181
+ return userdata->dbsqlok_retcode;
182
+ }
183
+
184
+ static void rb_tinytds_result_exec_helper(DBPROCESS *client) {
185
+ RETCODE dbsqlok_rc = rb_tinytds_result_ok_helper(client);
186
+ GET_CLIENT_USERDATA(client);
187
+ if (dbsqlok_rc == SUCCEED) {
188
+ /*
189
+ This is to just process each result set. Commands such as backup and
190
+ restore are not done when the first result set is returned, so we need to
191
+ exhaust the result sets before it is complete.
192
+ */
193
+ while (nogvl_dbresults(client) == SUCCEED) {
194
+ /*
195
+ If we don't loop through each row for calls to TinyTds::Result.do that
196
+ actually do return result sets, we will trigger error 20019 about trying
197
+ to execute a new command with pending results. Oh well.
198
+ */
199
+ while (dbnextrow(client) != NO_MORE_ROWS);
200
+ }
201
+ }
202
+ dbcancel(client);
203
+ userdata->dbcancel_sent = 1;
204
+ userdata->dbsql_sent = 0;
205
+ }
206
+
207
+ static VALUE rb_tinytds_result_fetch_row(VALUE self, ID timezone, int symbolize_keys, int as_array) {
208
+ VALUE row;
209
+ /* Storing Values */
210
+ unsigned int i;
211
+ /* Wrapper And Local Vars */
212
+ GET_RESULT_WRAPPER(self);
213
+ /* Create Empty Row */
214
+ row = as_array ? rb_ary_new2(rwrap->number_of_fields) : rb_hash_new();
215
+ for (i = 0; i < rwrap->number_of_fields; i++) {
216
+ VALUE val = Qnil;
217
+ int col = i+1;
218
+ int coltype = dbcoltype(rwrap->client, col);
219
+ BYTE *data = dbdata(rwrap->client, col);
220
+ DBINT data_len = dbdatlen(rwrap->client, col);
221
+ int null_val = ((data == NULL) && (data_len == 0));
222
+ if (!null_val) {
223
+ switch(coltype) {
224
+ case SYBINT1:
225
+ val = INT2FIX(*(DBTINYINT *)data);
226
+ break;
227
+ case SYBINT2:
228
+ val = INT2FIX(*(DBSMALLINT *)data);
229
+ break;
230
+ case SYBINT4:
231
+ val = INT2NUM(*(DBINT *)data);
232
+ break;
233
+ case SYBINT8:
234
+ val = LL2NUM(*(DBBIGINT *)data);
235
+ break;
236
+ case SYBBIT:
237
+ val = *(int *)data ? Qtrue : Qfalse;
238
+ break;
239
+ case SYBNUMERIC:
240
+ case SYBDECIMAL: {
241
+ DBTYPEINFO *data_info = dbcoltypeinfo(rwrap->client, col);
242
+ int data_slength = (int)data_info->precision + (int)data_info->scale + 1;
243
+ char converted_decimal[data_slength];
244
+ dbconvert(rwrap->client, coltype, data, data_len, SYBVARCHAR, (BYTE *)converted_decimal, -1);
245
+ val = rb_funcall(cKernel, intern_bigd, 1, rb_str_new2((char *)converted_decimal));
246
+ break;
247
+ }
248
+ case SYBFLT8: {
249
+ double col_to_double = *(double *)data;
250
+ val = (col_to_double == 0.000000) ? opt_float_zero : rb_float_new(col_to_double);
251
+ break;
252
+ }
253
+ case SYBREAL: {
254
+ float col_to_float = *(float *)data;
255
+ val = (col_to_float == 0.0) ? opt_float_zero : rb_float_new(col_to_float);
256
+ break;
257
+ }
258
+ case SYBMONEY: {
259
+ DBMONEY *money = (DBMONEY *)data;
260
+ char converted_money[25];
261
+ long long money_value = ((long long)money->mnyhigh << 32) | money->mnylow;
262
+ sprintf(converted_money, "%" LONG_LONG_FORMAT, money_value);
263
+ val = rb_funcall(cKernel, intern_bigd, 2, rb_str_new2(converted_money), opt_four);
264
+ val = rb_funcall(val, intern_divide, 1, opt_tenk);
265
+ break;
266
+ }
267
+ case SYBMONEY4: {
268
+ DBMONEY4 *money = (DBMONEY4 *)data;
269
+ char converted_money[20];
270
+ sprintf(converted_money, "%f", money->mny4 / 10000.0);
271
+ val = rb_funcall(cKernel, intern_bigd, 1, rb_str_new2(converted_money));
272
+ break;
273
+ }
274
+ case SYBBINARY:
275
+ case SYBIMAGE:
276
+ val = rb_str_new((char *)data, (long)data_len);
277
+ #ifdef HAVE_RUBY_ENCODING_H
278
+ rb_enc_associate(val, binaryEncoding);
279
+ #endif
280
+ break;
281
+ case 36: { // SYBUNIQUE
282
+ char converted_unique[37];
283
+ dbconvert(rwrap->client, coltype, data, 37, SYBVARCHAR, (BYTE *)converted_unique, -1);
284
+ val = ENCODED_STR_NEW2(converted_unique);
285
+ break;
286
+ }
287
+ case SYBDATETIME4: {
288
+ DBDATETIME new_data;
289
+ dbconvert(rwrap->client, coltype, data, data_len, SYBDATETIME, (BYTE *)&new_data, sizeof(new_data));
290
+ data = (BYTE *)&new_data;
291
+ data_len = sizeof(new_data);
292
+ }
293
+ case SYBDATETIME: {
294
+ DBDATEREC dr;
295
+ dbdatecrack(rwrap->client, &dr, (DBDATETIME *)data);
296
+ if (dr.year + dr.month + dr.day + dr.hour + dr.minute + dr.second + dr.millisecond != 0) {
297
+ val = rb_funcall(rb_cTime, timezone, 7, INT2NUM(dr.year), INT2NUM(dr.month), INT2NUM(dr.day), INT2NUM(dr.hour), INT2NUM(dr.minute), INT2NUM(dr.second), INT2NUM(dr.millisecond*1000));
298
+ }
299
+ break;
300
+ }
301
+ case SYBMSDATE:
302
+ case SYBMSTIME:
303
+ case SYBMSDATETIME2:
304
+ case SYBMSDATETIMEOFFSET: {
305
+ DBDATEREC2 dr2;
306
+ dbanydatecrack(rwrap->client, &dr2, coltype, data);
307
+ switch(coltype) {
308
+ case SYBMSDATE: {
309
+ val = rb_funcall(cDate, intern_new, 3, INT2NUM(dr2.year), INT2NUM(dr2.month), INT2NUM(dr2.day));
310
+ break;
311
+ }
312
+ case SYBMSTIME: {
313
+ VALUE rational_nsec = rb_Rational(INT2NUM(dr2.nanosecond), opt_onek);
314
+ val = rb_funcall(rb_cTime, timezone, 7, INT2NUM(1900), INT2NUM(1), INT2NUM(1), INT2NUM(dr2.hour), INT2NUM(dr2.minute), INT2NUM(dr2.second), rational_nsec);
315
+ break;
316
+ }
317
+ case SYBMSDATETIME2: {
318
+ VALUE rational_nsec = rb_Rational(INT2NUM(dr2.nanosecond), opt_onek);
319
+ val = rb_funcall(rb_cTime, timezone, 7, INT2NUM(dr2.year), INT2NUM(dr2.month), INT2NUM(dr2.day), INT2NUM(dr2.hour), INT2NUM(dr2.minute), INT2NUM(dr2.second), rational_nsec);
320
+ break;
321
+ }
322
+ case SYBMSDATETIMEOFFSET: {
323
+ long long numerator = ((long)dr2.second * (long long)1000000000) + (long long)dr2.nanosecond;
324
+ VALUE rational_sec = rb_Rational(LL2NUM(numerator), opt_onebil);
325
+ val = rb_funcall(rb_cTime, intern_new, 7, INT2NUM(dr2.year), INT2NUM(dr2.month), INT2NUM(dr2.day), INT2NUM(dr2.hour), INT2NUM(dr2.minute), rational_sec, INT2NUM(dr2.tzone*60));
326
+ break;
327
+ }
328
+ }
329
+ break;
330
+ }
331
+ case SYBCHAR:
332
+ case SYBTEXT:
333
+ val = ENCODED_STR_NEW(data, data_len);
334
+ break;
335
+ case 98: { // SYBVARIANT
336
+ if (data_len == 4) {
337
+ val = INT2NUM(*(DBINT *)data);
338
+ break;
339
+ } else {
340
+ val = ENCODED_STR_NEW(data, data_len);
341
+ break;
342
+ }
343
+ }
344
+ default:
345
+ val = ENCODED_STR_NEW(data, data_len);
346
+ break;
347
+ }
348
+ }
349
+ if (as_array) {
350
+ rb_ary_store(row, i, val);
351
+ } else {
352
+ VALUE key;
353
+ if (rwrap->number_of_results == 0) {
354
+ key = rb_ary_entry(rwrap->fields, i);
355
+ } else {
356
+ key = rb_ary_entry(rb_ary_entry(rwrap->fields, rwrap->number_of_results), i);
357
+ }
358
+ rb_hash_aset(row, key, val);
359
+ }
360
+ }
361
+ return row;
362
+ }
363
+
364
+
365
+ // TinyTds::Client (public)
366
+
367
+ static VALUE rb_tinytds_result_fields(VALUE self) {
368
+ RETCODE dbsqlok_rc, dbresults_rc;
369
+ VALUE fields_processed;
370
+ GET_RESULT_WRAPPER(self);
371
+ dbsqlok_rc = rb_tinytds_result_ok_helper(rwrap->client);
372
+ dbresults_rc = rb_tinytds_result_dbresults_retcode(self);
373
+ fields_processed = rb_ary_entry(rwrap->fields_processed, rwrap->number_of_results);
374
+ if ((dbsqlok_rc == SUCCEED) && (dbresults_rc == SUCCEED) && (fields_processed == Qnil)) {
375
+ /* Default query options. */
376
+ int symbolize_keys = 0;
377
+ VALUE qopts = rb_iv_get(self, "@query_options");
378
+ if (rb_hash_aref(qopts, sym_symbolize_keys) == Qtrue)
379
+ symbolize_keys = 1;
380
+ /* Set number_of_fields count for this result set. */
381
+ rwrap->number_of_fields = dbnumcols(rwrap->client);
382
+ if (rwrap->number_of_fields > 0) {
383
+ /* Create fields for this result set. */
384
+ unsigned int fldi = 0;
385
+ VALUE fields = rb_ary_new2(rwrap->number_of_fields);
386
+ for (fldi = 0; fldi < rwrap->number_of_fields; fldi++) {
387
+ char *colname = dbcolname(rwrap->client, fldi+1);
388
+ VALUE field = symbolize_keys ? rb_str_intern(ENCODED_STR_NEW2(colname)) : rb_obj_freeze(ENCODED_STR_NEW2(colname));
389
+ rb_ary_store(fields, fldi, field);
390
+ }
391
+ /* Store the fields. */
392
+ if (rwrap->number_of_results == 0) {
393
+ rwrap->fields = fields;
394
+ } else if (rwrap->number_of_results == 1) {
395
+ VALUE multi_rs_fields = rb_ary_new();
396
+ rb_ary_store(multi_rs_fields, 0, rwrap->fields);
397
+ rb_ary_store(multi_rs_fields, 1, fields);
398
+ rwrap->fields = multi_rs_fields;
399
+ } else {
400
+ rb_ary_store(rwrap->fields, rwrap->number_of_results, fields);
401
+ }
402
+ }
403
+ rb_ary_store(rwrap->fields_processed, rwrap->number_of_results, Qtrue);
404
+ }
405
+ return rwrap->fields;
406
+ }
407
+
408
+ static VALUE rb_tinytds_result_each(int argc, VALUE * argv, VALUE self) {
409
+ /* Local Vars */
410
+ VALUE qopts, opts, block;
411
+ ID timezone;
412
+ int symbolize_keys = 0, as_array = 0, cache_rows = 0, first = 0, empty_sets = 0;
413
+ tinytds_client_userdata *userdata;
414
+ GET_RESULT_WRAPPER(self);
415
+ userdata = (tinytds_client_userdata *)dbgetuserdata(rwrap->client);
416
+ /* Merge Options Hash To Query Options. Populate Opts & Block Var. */
417
+ qopts = rb_iv_get(self, "@query_options");
418
+ if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1)
419
+ qopts = rb_funcall(qopts, intern_merge, 1, opts);
420
+ rb_iv_set(self, "@query_options", qopts);
421
+ /* Locals From Options */
422
+ if (rb_hash_aref(qopts, sym_first) == Qtrue)
423
+ first = 1;
424
+ if (rb_hash_aref(qopts, sym_symbolize_keys) == Qtrue)
425
+ symbolize_keys = 1;
426
+ if (rb_hash_aref(qopts, sym_as) == sym_array)
427
+ as_array = 1;
428
+ if (rb_hash_aref(qopts, sym_cache_rows) == Qtrue)
429
+ cache_rows = 1;
430
+ if (rb_hash_aref(qopts, sym_timezone) == sym_local) {
431
+ timezone = intern_local;
432
+ } else if (rb_hash_aref(qopts, sym_timezone) == sym_utc) {
433
+ timezone = intern_utc;
434
+ } else {
435
+ rb_warn(":timezone option must be :utc or :local - defaulting to :local");
436
+ timezone = intern_local;
437
+ }
438
+ if (rb_hash_aref(qopts, sym_empty_sets) == Qtrue)
439
+ empty_sets = 1;
440
+ /* Make The Results Or Yield Existing */
441
+ if (NIL_P(rwrap->results)) {
442
+ RETCODE dbsqlok_rc, dbresults_rc;
443
+ rwrap->results = rb_ary_new();
444
+ dbsqlok_rc = rb_tinytds_result_ok_helper(rwrap->client);
445
+ dbresults_rc = rb_tinytds_result_dbresults_retcode(self);
446
+ while ((dbsqlok_rc == SUCCEED) && (dbresults_rc == SUCCEED)) {
447
+ int has_rows = (DBROWS(rwrap->client) == SUCCEED) ? 1 : 0;
448
+ if (has_rows || empty_sets || (rwrap->number_of_results == 0))
449
+ rb_tinytds_result_fields(self);
450
+ if ((has_rows || empty_sets) && rwrap->number_of_fields > 0) {
451
+ /* Create rows for this result set. */
452
+ unsigned long rowi = 0;
453
+ VALUE result = rb_ary_new();
454
+ while (nogvl_dbnextrow(rwrap->client) != NO_MORE_ROWS) {
455
+ VALUE row = rb_tinytds_result_fetch_row(self, timezone, symbolize_keys, as_array);
456
+ if (cache_rows)
457
+ rb_ary_store(result, rowi, row);
458
+ if (!NIL_P(block))
459
+ rb_yield(row);
460
+ if (first) {
461
+ dbcanquery(rwrap->client);
462
+ userdata->dbcancel_sent = 1;
463
+ }
464
+ rowi++;
465
+ }
466
+ rwrap->number_of_rows = rowi;
467
+ /* Store the result. */
468
+ if (cache_rows) {
469
+ if (rwrap->number_of_results == 0) {
470
+ rwrap->results = result;
471
+ } else if (rwrap->number_of_results == 1) {
472
+ VALUE multi_resultsets = rb_ary_new();
473
+ rb_ary_store(multi_resultsets, 0, rwrap->results);
474
+ rb_ary_store(multi_resultsets, 1, result);
475
+ rwrap->results = multi_resultsets;
476
+ } else {
477
+ rb_ary_store(rwrap->results, rwrap->number_of_results, result);
478
+ }
479
+ }
480
+ // If we find results increment the counter that helpers use and setup the next loop.
481
+ rwrap->number_of_results = rwrap->number_of_results + 1;
482
+ dbresults_rc = rb_tinytds_result_dbresults_retcode(self);
483
+ rb_ary_store(rwrap->fields_processed, rwrap->number_of_results, Qnil);
484
+ } else {
485
+ // If we do not find results, side step the rb_tinytds_result_dbresults_retcode helper and
486
+ // manually populate its memoized array while nullifing any memoized fields too before loop.
487
+ dbresults_rc = nogvl_dbresults(rwrap->client);
488
+ rb_ary_store(rwrap->dbresults_retcodes, rwrap->number_of_results, INT2FIX(dbresults_rc));
489
+ rb_ary_store(rwrap->fields_processed, rwrap->number_of_results, Qnil);
490
+ }
491
+ }
492
+ if (dbresults_rc == FAIL)
493
+ rb_warn("TinyTDS: Something in the dbresults() while loop set the return code to FAIL.\n");
494
+ userdata->dbsql_sent = 0;
495
+ } else if (!NIL_P(block)) {
496
+ unsigned long i;
497
+ for (i = 0; i < rwrap->number_of_rows; i++) {
498
+ rb_yield(rb_ary_entry(rwrap->results, i));
499
+ }
500
+ }
501
+ return rwrap->results;
502
+ }
503
+
504
+ static VALUE rb_tinytds_result_cancel(VALUE self) {
505
+ tinytds_client_userdata *userdata;
506
+ GET_RESULT_WRAPPER(self);
507
+ userdata = (tinytds_client_userdata *)dbgetuserdata(rwrap->client);
508
+ if (rwrap->client && !userdata->dbcancel_sent) {
509
+ rb_tinytds_result_ok_helper(rwrap->client);
510
+ dbcancel(rwrap->client);
511
+ userdata->dbcancel_sent = 1;
512
+ userdata->dbsql_sent = 0;
513
+ }
514
+ return Qtrue;
515
+ }
516
+
517
+ static VALUE rb_tinytds_result_do(VALUE self) {
518
+ GET_RESULT_WRAPPER(self);
519
+ if (rwrap->client) {
520
+ rb_tinytds_result_exec_helper(rwrap->client);
521
+ return LONG2NUM((long)dbcount(rwrap->client));
522
+ } else {
523
+ return Qnil;
524
+ }
525
+ }
526
+
527
+ static VALUE rb_tinytds_result_affected_rows(VALUE self) {
528
+ GET_RESULT_WRAPPER(self);
529
+ if (rwrap->client) {
530
+ return LONG2NUM((long)dbcount(rwrap->client));
531
+ } else {
532
+ return Qnil;
533
+ }
534
+ }
535
+
536
+ /* Duplicated in client.c */
537
+ static VALUE rb_tinytds_result_return_code(VALUE self) {
538
+ GET_RESULT_WRAPPER(self);
539
+ if (rwrap->client && dbhasretstat(rwrap->client)) {
540
+ return LONG2NUM((long)dbretstatus(rwrap->client));
541
+ } else {
542
+ return Qnil;
543
+ }
544
+ }
545
+
546
+ static VALUE rb_tinytds_result_insert(VALUE self) {
547
+ GET_RESULT_WRAPPER(self);
548
+ if (rwrap->client) {
549
+ VALUE identity = Qnil;
550
+ rb_tinytds_result_exec_helper(rwrap->client);
551
+ dbcmd(rwrap->client, rwrap->cwrap->identity_insert_sql);
552
+ if (nogvl_dbsqlexec(rwrap->client) != FAIL
553
+ && nogvl_dbresults(rwrap->client) != FAIL
554
+ && DBROWS(rwrap->client) != FAIL) {
555
+ while (nogvl_dbnextrow(rwrap->client) != NO_MORE_ROWS) {
556
+ int col = 1;
557
+ BYTE *data = dbdata(rwrap->client, col);
558
+ DBINT data_len = dbdatlen(rwrap->client, col);
559
+ int null_val = ((data == NULL) && (data_len == 0));
560
+ if (!null_val)
561
+ identity = LL2NUM(*(DBBIGINT *)data);
562
+ }
563
+ }
564
+ return identity;
565
+ } else {
566
+ return Qnil;
567
+ }
568
+ }
569
+
570
+
571
+ // Lib Init
572
+
573
+ void init_tinytds_result() {
574
+ /* Data Classes */
575
+ cKernel = rb_const_get(rb_cObject, rb_intern("Kernel"));
576
+ cDate = rb_const_get(rb_cObject, rb_intern("Date"));
577
+ /* Define TinyTds::Result */
578
+ cTinyTdsResult = rb_define_class_under(mTinyTds, "Result", rb_cObject);
579
+ rb_undef_alloc_func(cTinyTdsResult);
580
+ /* Define TinyTds::Result Public Methods */
581
+ rb_define_method(cTinyTdsResult, "fields", rb_tinytds_result_fields, 0);
582
+ rb_define_method(cTinyTdsResult, "each", rb_tinytds_result_each, -1);
583
+ rb_define_method(cTinyTdsResult, "cancel", rb_tinytds_result_cancel, 0);
584
+ rb_define_method(cTinyTdsResult, "do", rb_tinytds_result_do, 0);
585
+ rb_define_method(cTinyTdsResult, "affected_rows", rb_tinytds_result_affected_rows, 0);
586
+ rb_define_method(cTinyTdsResult, "return_code", rb_tinytds_result_return_code, 0);
587
+ rb_define_method(cTinyTdsResult, "insert", rb_tinytds_result_insert, 0);
588
+ /* Intern String Helpers */
589
+ intern_new = rb_intern("new");
590
+ intern_utc = rb_intern("utc");
591
+ intern_local = rb_intern("local");
592
+ intern_merge = rb_intern("merge");
593
+ intern_localtime = rb_intern("localtime");
594
+ intern_civil = rb_intern("civil");
595
+ intern_new_offset = rb_intern("new_offset");
596
+ intern_plus = rb_intern("+");
597
+ intern_divide = rb_intern("/");
598
+ intern_bigd = rb_intern("BigDecimal");
599
+ /* Symbol Helpers */
600
+ sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
601
+ sym_as = ID2SYM(rb_intern("as"));
602
+ sym_array = ID2SYM(rb_intern("array"));
603
+ sym_cache_rows = ID2SYM(rb_intern("cache_rows"));
604
+ sym_first = ID2SYM(rb_intern("first"));
605
+ sym_local = ID2SYM(intern_local);
606
+ sym_utc = ID2SYM(intern_utc);
607
+ sym_timezone = ID2SYM(rb_intern("timezone"));
608
+ sym_empty_sets = ID2SYM(rb_intern("empty_sets"));
609
+ /* Data Conversion Options */
610
+ opt_decimal_zero = rb_str_new2("0.0");
611
+ rb_global_variable(&opt_decimal_zero);
612
+ opt_float_zero = rb_float_new((double)0);
613
+ rb_global_variable(&opt_float_zero);
614
+ opt_one = INT2NUM(1);
615
+ opt_zero = INT2NUM(0);
616
+ opt_four = INT2NUM(4);
617
+ opt_19hdr = INT2NUM(1900);
618
+ opt_onek = INT2NUM(1000);
619
+ opt_tenk = INT2NUM(10000);
620
+ opt_onemil = INT2NUM(1000000);
621
+ opt_onebil = INT2NUM(1000000000);
622
+ /* Encoding */
623
+ #ifdef HAVE_RUBY_ENCODING_H
624
+ binaryEncoding = rb_enc_find("binary");
625
+ #endif
626
+ }
@@ -0,0 +1,32 @@
1
+
2
+ #ifndef TINYTDS_RESULT_H
3
+ #define TINYTDS_RESULT_H
4
+
5
+ void init_tinytds_result();
6
+ VALUE rb_tinytds_new_result_obj(tinytds_client_wrapper *cwrap);
7
+
8
+ typedef struct {
9
+ tinytds_client_wrapper *cwrap;
10
+ DBPROCESS *client;
11
+ VALUE local_offset;
12
+ VALUE fields;
13
+ VALUE fields_processed;
14
+ VALUE results;
15
+ rb_encoding *encoding;
16
+ VALUE dbresults_retcodes;
17
+ unsigned int number_of_results;
18
+ unsigned int number_of_fields;
19
+ unsigned long number_of_rows;
20
+ } tinytds_result_wrapper;
21
+
22
+
23
+ // Lib Macros
24
+
25
+ #define GET_RESULT_WRAPPER(self) \
26
+ tinytds_result_wrapper *rwrap; \
27
+ Data_Get_Struct(self, tinytds_result_wrapper, rwrap)
28
+
29
+
30
+
31
+
32
+ #endif
@@ -0,0 +1,15 @@
1
+ #include <tiny_tds_ext.h>
2
+
3
+ #ifndef DBVERSION_73
4
+ #error "DBVERSION_73 is not defined. Aborting compilation."
5
+ #endif
6
+
7
+ VALUE mTinyTds, cTinyTdsError;
8
+
9
+ void Init_tiny_tds() {
10
+ mTinyTds = rb_define_module("TinyTds");
11
+ cTinyTdsError = rb_const_get(mTinyTds, rb_intern("Error"));
12
+ init_tinytds_client();
13
+ init_tinytds_result();
14
+ }
15
+
@@ -0,0 +1,17 @@
1
+ #ifndef TINYTDS_EXT
2
+ #define TINYTDS_EXT
3
+
4
+ #undef SYBDBLIB
5
+ #define MSDBLIB 1
6
+
7
+ #include <ruby.h>
8
+ #include <ruby/encoding.h>
9
+ #include <ruby/version.h>
10
+ #include <ruby/thread.h>
11
+ #include <sybfront.h>
12
+ #include <sybdb.h>
13
+
14
+ #include <client.h>
15
+ #include <result.h>
16
+
17
+ #endif
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file