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