tiny_tds 2.1.6-x64-mingw-ucrt

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +407 -0
  3. data/.codeclimate.yml +20 -0
  4. data/.gitattributes +1 -0
  5. data/.gitignore +22 -0
  6. data/.rubocop.yml +31 -0
  7. data/CHANGELOG.md +280 -0
  8. data/CODE_OF_CONDUCT.md +31 -0
  9. data/Gemfile +2 -0
  10. data/ISSUE_TEMPLATE.md +38 -0
  11. data/MIT-LICENSE +23 -0
  12. data/README.md +504 -0
  13. data/Rakefile +62 -0
  14. data/VERSION +1 -0
  15. data/bin/defncopy-ttds +3 -0
  16. data/bin/tsql-ttds +3 -0
  17. data/docker-compose.yml +34 -0
  18. data/exe/.keep +0 -0
  19. data/ext/tiny_tds/client.c +499 -0
  20. data/ext/tiny_tds/client.h +53 -0
  21. data/ext/tiny_tds/extconf.rb +92 -0
  22. data/ext/tiny_tds/extconsts.rb +15 -0
  23. data/ext/tiny_tds/result.c +634 -0
  24. data/ext/tiny_tds/result.h +32 -0
  25. data/ext/tiny_tds/tiny_tds_ext.c +12 -0
  26. data/ext/tiny_tds/tiny_tds_ext.h +17 -0
  27. data/lib/tiny_tds/3.1/tiny_tds.so +0 -0
  28. data/lib/tiny_tds/3.2/tiny_tds.so +0 -0
  29. data/lib/tiny_tds/bin.rb +104 -0
  30. data/lib/tiny_tds/client.rb +136 -0
  31. data/lib/tiny_tds/error.rb +14 -0
  32. data/lib/tiny_tds/gem.rb +27 -0
  33. data/lib/tiny_tds/result.rb +7 -0
  34. data/lib/tiny_tds/version.rb +3 -0
  35. data/lib/tiny_tds.rb +61 -0
  36. data/patches/freetds/1.00.27/0001-mingw_missing_inet_pton.diff +34 -0
  37. data/patches/freetds/1.00.27/0002-Don-t-use-MSYS2-file-libws2_32.diff +28 -0
  38. data/patches/libiconv/1.14/1-avoid-gets-error.patch +17 -0
  39. data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/bsqldb.exe +0 -0
  40. data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/datacopy.exe +0 -0
  41. data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/defncopy.exe +0 -0
  42. data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/freebcp.exe +0 -0
  43. data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/libct-4.dll +0 -0
  44. data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/libsybdb-5.dll +0 -0
  45. data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/osql +388 -0
  46. data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/tdspool.exe +0 -0
  47. data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/tsql.exe +0 -0
  48. data/ports/x64-mingw-ucrt/freetds/1.1.24/lib/libct.dll.a +0 -0
  49. data/ports/x64-mingw-ucrt/freetds/1.1.24/lib/libct.la +41 -0
  50. data/ports/x64-mingw-ucrt/freetds/1.1.24/lib/libsybdb.dll.a +0 -0
  51. data/ports/x64-mingw-ucrt/freetds/1.1.24/lib/libsybdb.la +41 -0
  52. data/ports/x64-mingw-ucrt/libiconv/1.15/bin/iconv.exe +0 -0
  53. data/ports/x64-mingw-ucrt/libiconv/1.15/bin/libcharset-1.dll +0 -0
  54. data/ports/x64-mingw-ucrt/libiconv/1.15/bin/libiconv-2.dll +0 -0
  55. data/ports/x64-mingw-ucrt/libiconv/1.15/lib/charset.alias +4 -0
  56. data/ports/x64-mingw-ucrt/libiconv/1.15/lib/libcharset.dll.a +0 -0
  57. data/ports/x64-mingw-ucrt/libiconv/1.15/lib/libcharset.la +41 -0
  58. data/ports/x64-mingw-ucrt/libiconv/1.15/lib/libiconv.dll.a +0 -0
  59. data/ports/x64-mingw-ucrt/libiconv/1.15/lib/libiconv.la +41 -0
  60. data/ports/x64-mingw-ucrt/openssl/1.1.1s/bin/c_rehash +251 -0
  61. data/ports/x64-mingw-ucrt/openssl/1.1.1s/bin/libcrypto-1_1-x64.dll +0 -0
  62. data/ports/x64-mingw-ucrt/openssl/1.1.1s/bin/libssl-1_1-x64.dll +0 -0
  63. data/ports/x64-mingw-ucrt/openssl/1.1.1s/bin/openssl.exe +0 -0
  64. data/ports/x64-mingw-ucrt/openssl/1.1.1s/lib/libcrypto.a +0 -0
  65. data/ports/x64-mingw-ucrt/openssl/1.1.1s/lib/libcrypto.dll.a +0 -0
  66. data/ports/x64-mingw-ucrt/openssl/1.1.1s/lib/libssl.a +0 -0
  67. data/ports/x64-mingw-ucrt/openssl/1.1.1s/lib/libssl.dll.a +0 -0
  68. data/setup_cimgruby_dev.sh +25 -0
  69. data/start_dev.sh +21 -0
  70. data/tasks/native_gem.rake +23 -0
  71. data/tasks/package.rake +8 -0
  72. data/tasks/ports/freetds.rb +37 -0
  73. data/tasks/ports/libiconv.rb +26 -0
  74. data/tasks/ports/openssl.rb +62 -0
  75. data/tasks/ports/recipe.rb +64 -0
  76. data/tasks/ports.rake +108 -0
  77. data/tasks/test.rake +9 -0
  78. data/test/benchmark/query.rb +77 -0
  79. data/test/benchmark/query_odbc.rb +106 -0
  80. data/test/benchmark/query_tinytds.rb +126 -0
  81. data/test/bin/install-freetds.sh +20 -0
  82. data/test/bin/install-mssql.ps1 +31 -0
  83. data/test/bin/install-mssqltools.sh +9 -0
  84. data/test/bin/install-openssl.sh +18 -0
  85. data/test/bin/setup_tinytds_db.sh +7 -0
  86. data/test/bin/setup_volume_permissions.sh +10 -0
  87. data/test/client_test.rb +275 -0
  88. data/test/gem_test.rb +177 -0
  89. data/test/result_test.rb +814 -0
  90. data/test/schema/1px.gif +0 -0
  91. data/test/schema/sqlserver_2000.sql +140 -0
  92. data/test/schema/sqlserver_2005.sql +140 -0
  93. data/test/schema/sqlserver_2008.sql +140 -0
  94. data/test/schema/sqlserver_2014.sql +140 -0
  95. data/test/schema/sqlserver_2016.sql +140 -0
  96. data/test/schema/sqlserver_azure.sql +140 -0
  97. data/test/schema/sybase_ase.sql +138 -0
  98. data/test/schema_test.rb +443 -0
  99. data/test/sql/db-create.sql +18 -0
  100. data/test/sql/db-login.sql +38 -0
  101. data/test/test_helper.rb +280 -0
  102. data/test/thread_test.rb +98 -0
  103. data/tiny_tds.gemspec +31 -0
  104. metadata +267 -0
@@ -0,0 +1,634 @@
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 40: // SYBMSDATE
302
+ case 41: // SYBMSTIME
303
+ case 42: // SYBMSDATETIME2
304
+ case 43: { // SYBMSDATETIMEOFFSET
305
+ #ifdef DBVERSION_73
306
+ if (dbtds(rwrap->client) >= DBTDS_7_3) {
307
+ DBDATEREC2 dr2;
308
+ dbanydatecrack(rwrap->client, &dr2, coltype, data);
309
+ switch(coltype) {
310
+ case 40: { // SYBMSDATE
311
+ val = rb_funcall(cDate, intern_new, 3, INT2NUM(dr2.year), INT2NUM(dr2.month), INT2NUM(dr2.day));
312
+ break;
313
+ }
314
+ case 41: { // SYBMSTIME
315
+ VALUE rational_nsec = rb_Rational(INT2NUM(dr2.nanosecond), opt_onek);
316
+ val = rb_funcall(rb_cTime, timezone, 7, INT2NUM(1900), INT2NUM(1), INT2NUM(1), INT2NUM(dr2.hour), INT2NUM(dr2.minute), INT2NUM(dr2.second), rational_nsec);
317
+ break;
318
+ }
319
+ case 42: { // SYBMSDATETIME2
320
+ VALUE rational_nsec = rb_Rational(INT2NUM(dr2.nanosecond), opt_onek);
321
+ 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);
322
+ break;
323
+ }
324
+ case 43: { // SYBMSDATETIMEOFFSET
325
+ long long numerator = ((long)dr2.second * (long long)1000000000) + (long long)dr2.nanosecond;
326
+ VALUE rational_sec = rb_Rational(LL2NUM(numerator), opt_onebil);
327
+ 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));
328
+ break;
329
+ }
330
+ }
331
+ } else {
332
+ val = ENCODED_STR_NEW(data, data_len);
333
+ }
334
+ #else
335
+ val = ENCODED_STR_NEW(data, data_len);
336
+ #endif
337
+ break;
338
+ }
339
+ case SYBCHAR:
340
+ case SYBTEXT:
341
+ val = ENCODED_STR_NEW(data, data_len);
342
+ break;
343
+ case 98: { // SYBVARIANT
344
+ if (data_len == 4) {
345
+ val = INT2NUM(*(DBINT *)data);
346
+ break;
347
+ } else {
348
+ val = ENCODED_STR_NEW(data, data_len);
349
+ break;
350
+ }
351
+ }
352
+ default:
353
+ val = ENCODED_STR_NEW(data, data_len);
354
+ break;
355
+ }
356
+ }
357
+ if (as_array) {
358
+ rb_ary_store(row, i, val);
359
+ } else {
360
+ VALUE key;
361
+ if (rwrap->number_of_results == 0) {
362
+ key = rb_ary_entry(rwrap->fields, i);
363
+ } else {
364
+ key = rb_ary_entry(rb_ary_entry(rwrap->fields, rwrap->number_of_results), i);
365
+ }
366
+ rb_hash_aset(row, key, val);
367
+ }
368
+ }
369
+ return row;
370
+ }
371
+
372
+
373
+ // TinyTds::Client (public)
374
+
375
+ static VALUE rb_tinytds_result_fields(VALUE self) {
376
+ RETCODE dbsqlok_rc, dbresults_rc;
377
+ VALUE fields_processed;
378
+ GET_RESULT_WRAPPER(self);
379
+ dbsqlok_rc = rb_tinytds_result_ok_helper(rwrap->client);
380
+ dbresults_rc = rb_tinytds_result_dbresults_retcode(self);
381
+ fields_processed = rb_ary_entry(rwrap->fields_processed, rwrap->number_of_results);
382
+ if ((dbsqlok_rc == SUCCEED) && (dbresults_rc == SUCCEED) && (fields_processed == Qnil)) {
383
+ /* Default query options. */
384
+ int symbolize_keys = 0;
385
+ VALUE qopts = rb_iv_get(self, "@query_options");
386
+ if (rb_hash_aref(qopts, sym_symbolize_keys) == Qtrue)
387
+ symbolize_keys = 1;
388
+ /* Set number_of_fields count for this result set. */
389
+ rwrap->number_of_fields = dbnumcols(rwrap->client);
390
+ if (rwrap->number_of_fields > 0) {
391
+ /* Create fields for this result set. */
392
+ unsigned int fldi = 0;
393
+ VALUE fields = rb_ary_new2(rwrap->number_of_fields);
394
+ for (fldi = 0; fldi < rwrap->number_of_fields; fldi++) {
395
+ char *colname = dbcolname(rwrap->client, fldi+1);
396
+ VALUE field = symbolize_keys ? rb_str_intern(ENCODED_STR_NEW2(colname)) : rb_obj_freeze(ENCODED_STR_NEW2(colname));
397
+ rb_ary_store(fields, fldi, field);
398
+ }
399
+ /* Store the fields. */
400
+ if (rwrap->number_of_results == 0) {
401
+ rwrap->fields = fields;
402
+ } else if (rwrap->number_of_results == 1) {
403
+ VALUE multi_rs_fields = rb_ary_new();
404
+ rb_ary_store(multi_rs_fields, 0, rwrap->fields);
405
+ rb_ary_store(multi_rs_fields, 1, fields);
406
+ rwrap->fields = multi_rs_fields;
407
+ } else {
408
+ rb_ary_store(rwrap->fields, rwrap->number_of_results, fields);
409
+ }
410
+ }
411
+ rb_ary_store(rwrap->fields_processed, rwrap->number_of_results, Qtrue);
412
+ }
413
+ return rwrap->fields;
414
+ }
415
+
416
+ static VALUE rb_tinytds_result_each(int argc, VALUE * argv, VALUE self) {
417
+ /* Local Vars */
418
+ VALUE qopts, opts, block;
419
+ ID timezone;
420
+ int symbolize_keys = 0, as_array = 0, cache_rows = 0, first = 0, empty_sets = 0;
421
+ tinytds_client_userdata *userdata;
422
+ GET_RESULT_WRAPPER(self);
423
+ userdata = (tinytds_client_userdata *)dbgetuserdata(rwrap->client);
424
+ /* Merge Options Hash To Query Options. Populate Opts & Block Var. */
425
+ qopts = rb_iv_get(self, "@query_options");
426
+ if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1)
427
+ qopts = rb_funcall(qopts, intern_merge, 1, opts);
428
+ rb_iv_set(self, "@query_options", qopts);
429
+ /* Locals From Options */
430
+ if (rb_hash_aref(qopts, sym_first) == Qtrue)
431
+ first = 1;
432
+ if (rb_hash_aref(qopts, sym_symbolize_keys) == Qtrue)
433
+ symbolize_keys = 1;
434
+ if (rb_hash_aref(qopts, sym_as) == sym_array)
435
+ as_array = 1;
436
+ if (rb_hash_aref(qopts, sym_cache_rows) == Qtrue)
437
+ cache_rows = 1;
438
+ if (rb_hash_aref(qopts, sym_timezone) == sym_local) {
439
+ timezone = intern_local;
440
+ } else if (rb_hash_aref(qopts, sym_timezone) == sym_utc) {
441
+ timezone = intern_utc;
442
+ } else {
443
+ rb_warn(":timezone option must be :utc or :local - defaulting to :local");
444
+ timezone = intern_local;
445
+ }
446
+ if (rb_hash_aref(qopts, sym_empty_sets) == Qtrue)
447
+ empty_sets = 1;
448
+ /* Make The Results Or Yield Existing */
449
+ if (NIL_P(rwrap->results)) {
450
+ RETCODE dbsqlok_rc, dbresults_rc;
451
+ rwrap->results = rb_ary_new();
452
+ dbsqlok_rc = rb_tinytds_result_ok_helper(rwrap->client);
453
+ dbresults_rc = rb_tinytds_result_dbresults_retcode(self);
454
+ while ((dbsqlok_rc == SUCCEED) && (dbresults_rc == SUCCEED)) {
455
+ int has_rows = (DBROWS(rwrap->client) == SUCCEED) ? 1 : 0;
456
+ if (has_rows || empty_sets || (rwrap->number_of_results == 0))
457
+ rb_tinytds_result_fields(self);
458
+ if ((has_rows || empty_sets) && rwrap->number_of_fields > 0) {
459
+ /* Create rows for this result set. */
460
+ unsigned long rowi = 0;
461
+ VALUE result = rb_ary_new();
462
+ while (nogvl_dbnextrow(rwrap->client) != NO_MORE_ROWS) {
463
+ VALUE row = rb_tinytds_result_fetch_row(self, timezone, symbolize_keys, as_array);
464
+ if (cache_rows)
465
+ rb_ary_store(result, rowi, row);
466
+ if (!NIL_P(block))
467
+ rb_yield(row);
468
+ if (first) {
469
+ dbcanquery(rwrap->client);
470
+ userdata->dbcancel_sent = 1;
471
+ }
472
+ rowi++;
473
+ }
474
+ rwrap->number_of_rows = rowi;
475
+ /* Store the result. */
476
+ if (cache_rows) {
477
+ if (rwrap->number_of_results == 0) {
478
+ rwrap->results = result;
479
+ } else if (rwrap->number_of_results == 1) {
480
+ VALUE multi_resultsets = rb_ary_new();
481
+ rb_ary_store(multi_resultsets, 0, rwrap->results);
482
+ rb_ary_store(multi_resultsets, 1, result);
483
+ rwrap->results = multi_resultsets;
484
+ } else {
485
+ rb_ary_store(rwrap->results, rwrap->number_of_results, result);
486
+ }
487
+ }
488
+ // If we find results increment the counter that helpers use and setup the next loop.
489
+ rwrap->number_of_results = rwrap->number_of_results + 1;
490
+ dbresults_rc = rb_tinytds_result_dbresults_retcode(self);
491
+ rb_ary_store(rwrap->fields_processed, rwrap->number_of_results, Qnil);
492
+ } else {
493
+ // If we do not find results, side step the rb_tinytds_result_dbresults_retcode helper and
494
+ // manually populate its memoized array while nullifing any memoized fields too before loop.
495
+ dbresults_rc = nogvl_dbresults(rwrap->client);
496
+ rb_ary_store(rwrap->dbresults_retcodes, rwrap->number_of_results, INT2FIX(dbresults_rc));
497
+ rb_ary_store(rwrap->fields_processed, rwrap->number_of_results, Qnil);
498
+ }
499
+ }
500
+ if (dbresults_rc == FAIL)
501
+ rb_warn("TinyTDS: Something in the dbresults() while loop set the return code to FAIL.\n");
502
+ userdata->dbsql_sent = 0;
503
+ } else if (!NIL_P(block)) {
504
+ unsigned long i;
505
+ for (i = 0; i < rwrap->number_of_rows; i++) {
506
+ rb_yield(rb_ary_entry(rwrap->results, i));
507
+ }
508
+ }
509
+ return rwrap->results;
510
+ }
511
+
512
+ static VALUE rb_tinytds_result_cancel(VALUE self) {
513
+ tinytds_client_userdata *userdata;
514
+ GET_RESULT_WRAPPER(self);
515
+ userdata = (tinytds_client_userdata *)dbgetuserdata(rwrap->client);
516
+ if (rwrap->client && !userdata->dbcancel_sent) {
517
+ rb_tinytds_result_ok_helper(rwrap->client);
518
+ dbcancel(rwrap->client);
519
+ userdata->dbcancel_sent = 1;
520
+ userdata->dbsql_sent = 0;
521
+ }
522
+ return Qtrue;
523
+ }
524
+
525
+ static VALUE rb_tinytds_result_do(VALUE self) {
526
+ GET_RESULT_WRAPPER(self);
527
+ if (rwrap->client) {
528
+ rb_tinytds_result_exec_helper(rwrap->client);
529
+ return LONG2NUM((long)dbcount(rwrap->client));
530
+ } else {
531
+ return Qnil;
532
+ }
533
+ }
534
+
535
+ static VALUE rb_tinytds_result_affected_rows(VALUE self) {
536
+ GET_RESULT_WRAPPER(self);
537
+ if (rwrap->client) {
538
+ return LONG2NUM((long)dbcount(rwrap->client));
539
+ } else {
540
+ return Qnil;
541
+ }
542
+ }
543
+
544
+ /* Duplicated in client.c */
545
+ static VALUE rb_tinytds_result_return_code(VALUE self) {
546
+ GET_RESULT_WRAPPER(self);
547
+ if (rwrap->client && dbhasretstat(rwrap->client)) {
548
+ return LONG2NUM((long)dbretstatus(rwrap->client));
549
+ } else {
550
+ return Qnil;
551
+ }
552
+ }
553
+
554
+ static VALUE rb_tinytds_result_insert(VALUE self) {
555
+ GET_RESULT_WRAPPER(self);
556
+ if (rwrap->client) {
557
+ VALUE identity = Qnil;
558
+ rb_tinytds_result_exec_helper(rwrap->client);
559
+ dbcmd(rwrap->client, rwrap->cwrap->identity_insert_sql);
560
+ if (nogvl_dbsqlexec(rwrap->client) != FAIL
561
+ && nogvl_dbresults(rwrap->client) != FAIL
562
+ && DBROWS(rwrap->client) != FAIL) {
563
+ while (nogvl_dbnextrow(rwrap->client) != NO_MORE_ROWS) {
564
+ int col = 1;
565
+ BYTE *data = dbdata(rwrap->client, col);
566
+ DBINT data_len = dbdatlen(rwrap->client, col);
567
+ int null_val = ((data == NULL) && (data_len == 0));
568
+ if (!null_val)
569
+ identity = LL2NUM(*(DBBIGINT *)data);
570
+ }
571
+ }
572
+ return identity;
573
+ } else {
574
+ return Qnil;
575
+ }
576
+ }
577
+
578
+
579
+ // Lib Init
580
+
581
+ void init_tinytds_result() {
582
+ /* Data Classes */
583
+ cKernel = rb_const_get(rb_cObject, rb_intern("Kernel"));
584
+ cDate = rb_const_get(rb_cObject, rb_intern("Date"));
585
+ /* Define TinyTds::Result */
586
+ cTinyTdsResult = rb_define_class_under(mTinyTds, "Result", rb_cObject);
587
+ rb_undef_alloc_func(cTinyTdsResult);
588
+ /* Define TinyTds::Result Public Methods */
589
+ rb_define_method(cTinyTdsResult, "fields", rb_tinytds_result_fields, 0);
590
+ rb_define_method(cTinyTdsResult, "each", rb_tinytds_result_each, -1);
591
+ rb_define_method(cTinyTdsResult, "cancel", rb_tinytds_result_cancel, 0);
592
+ rb_define_method(cTinyTdsResult, "do", rb_tinytds_result_do, 0);
593
+ rb_define_method(cTinyTdsResult, "affected_rows", rb_tinytds_result_affected_rows, 0);
594
+ rb_define_method(cTinyTdsResult, "return_code", rb_tinytds_result_return_code, 0);
595
+ rb_define_method(cTinyTdsResult, "insert", rb_tinytds_result_insert, 0);
596
+ /* Intern String Helpers */
597
+ intern_new = rb_intern("new");
598
+ intern_utc = rb_intern("utc");
599
+ intern_local = rb_intern("local");
600
+ intern_merge = rb_intern("merge");
601
+ intern_localtime = rb_intern("localtime");
602
+ intern_civil = rb_intern("civil");
603
+ intern_new_offset = rb_intern("new_offset");
604
+ intern_plus = rb_intern("+");
605
+ intern_divide = rb_intern("/");
606
+ intern_bigd = rb_intern("BigDecimal");
607
+ /* Symbol Helpers */
608
+ sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
609
+ sym_as = ID2SYM(rb_intern("as"));
610
+ sym_array = ID2SYM(rb_intern("array"));
611
+ sym_cache_rows = ID2SYM(rb_intern("cache_rows"));
612
+ sym_first = ID2SYM(rb_intern("first"));
613
+ sym_local = ID2SYM(intern_local);
614
+ sym_utc = ID2SYM(intern_utc);
615
+ sym_timezone = ID2SYM(rb_intern("timezone"));
616
+ sym_empty_sets = ID2SYM(rb_intern("empty_sets"));
617
+ /* Data Conversion Options */
618
+ opt_decimal_zero = rb_str_new2("0.0");
619
+ rb_global_variable(&opt_decimal_zero);
620
+ opt_float_zero = rb_float_new((double)0);
621
+ rb_global_variable(&opt_float_zero);
622
+ opt_one = INT2NUM(1);
623
+ opt_zero = INT2NUM(0);
624
+ opt_four = INT2NUM(4);
625
+ opt_19hdr = INT2NUM(1900);
626
+ opt_onek = INT2NUM(1000);
627
+ opt_tenk = INT2NUM(10000);
628
+ opt_onemil = INT2NUM(1000000);
629
+ opt_onebil = INT2NUM(1000000000);
630
+ /* Encoding */
631
+ #ifdef HAVE_RUBY_ENCODING_H
632
+ binaryEncoding = rb_enc_find("binary");
633
+ #endif
634
+ }
@@ -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
Binary file
Binary file