tiny_tds 0.4.5.rc1-x86-mingw32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,493 @@
1
+
2
+ #include <tiny_tds_ext.h>
3
+
4
+ VALUE cTinyTdsResult;
5
+ extern VALUE mTinyTds, cTinyTdsClient, cTinyTdsError;
6
+ VALUE cBigDecimal, cDate, cDateTime;
7
+ VALUE opt_decimal_zero, opt_float_zero, opt_one, opt_zero, opt_four, opt_19hdr, opt_tenk, opt_onemil;
8
+ int opt_ruby_186;
9
+ static ID intern_new, intern_utc, intern_local, intern_localtime, intern_merge,
10
+ intern_civil, intern_new_offset, intern_plus, intern_divide, intern_Rational;
11
+ static ID sym_symbolize_keys, sym_as, sym_array, sym_cache_rows, sym_first, sym_timezone, sym_local, sym_utc;
12
+
13
+
14
+ // Lib Macros
15
+
16
+ #ifdef HAVE_RUBY_ENCODING_H
17
+ rb_encoding *binaryEncoding;
18
+ #define ENCODED_STR_NEW(_data, _len) ({ \
19
+ VALUE _val = rb_str_new((char *)_data, (long)_len); \
20
+ rb_enc_associate(_val, rwrap->encoding); \
21
+ _val; \
22
+ })
23
+ #define ENCODED_STR_NEW2(_data2) ({ \
24
+ VALUE _val = rb_str_new2((char *)_data2); \
25
+ rb_enc_associate(_val, rwrap->encoding); \
26
+ _val; \
27
+ })
28
+ #else
29
+ #define ENCODED_STR_NEW(_data, _len) \
30
+ rb_str_new((char *)_data, (long)_len)
31
+ #define ENCODED_STR_NEW2(_data2) \
32
+ rb_str_new2((char *)_data2)
33
+ #endif
34
+
35
+
36
+ // Lib Backend (Memory Management)
37
+
38
+ static void rb_tinytds_result_mark(void *ptr) {
39
+ tinytds_result_wrapper *rwrap = (tinytds_result_wrapper *)ptr;
40
+ if (rwrap) {
41
+ rb_gc_mark(rwrap->local_offset);
42
+ rb_gc_mark(rwrap->fields);
43
+ rb_gc_mark(rwrap->fields_processed);
44
+ rb_gc_mark(rwrap->results);
45
+ rb_gc_mark(rwrap->dbresults_retcodes);
46
+ }
47
+ }
48
+
49
+ static void rb_tinytds_result_free(void *ptr) {
50
+ tinytds_result_wrapper *rwrap = (tinytds_result_wrapper *)ptr;
51
+ xfree(ptr);
52
+ }
53
+
54
+ VALUE rb_tinytds_new_result_obj(DBPROCESS *c) {
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->client = c;
59
+ rwrap->local_offset = Qnil;
60
+ rwrap->fields = rb_ary_new();
61
+ rwrap->fields_processed = rb_ary_new();
62
+ rwrap->results = Qnil;
63
+ rwrap->dbresults_retcodes = rb_ary_new();
64
+ rwrap->number_of_results = 0;
65
+ rwrap->number_of_fields = 0;
66
+ rwrap->number_of_rows = 0;
67
+ rb_obj_call_init(obj, 0, NULL);
68
+ return obj;
69
+ }
70
+
71
+
72
+ // Lib Backend (Helpers)
73
+
74
+ static RETCODE rb_tinytds_result_dbresults_retcode(VALUE self) {
75
+ GET_RESULT_WRAPPER(self);
76
+ VALUE ruby_rc;
77
+ RETCODE db_rc;
78
+ ruby_rc = rb_ary_entry(rwrap->dbresults_retcodes, rwrap->number_of_results);
79
+ if (NIL_P(ruby_rc)) {
80
+ db_rc = dbresults(rwrap->client);
81
+ ruby_rc = INT2FIX(db_rc);
82
+ rb_ary_store(rwrap->dbresults_retcodes, rwrap->number_of_results, ruby_rc);
83
+ } else {
84
+ db_rc = FIX2INT(ruby_rc);
85
+ }
86
+ return db_rc;
87
+ }
88
+
89
+ static RETCODE rb_tinytds_result_ok_helper(DBPROCESS *client) {
90
+ GET_CLIENT_USERDATA(client);
91
+ if (userdata->dbsqlok_sent == 0) {
92
+ userdata->dbsqlok_retcode = dbsqlok(client);
93
+ userdata->dbsqlok_sent = 1;
94
+ }
95
+ return userdata->dbsqlok_retcode;
96
+ }
97
+
98
+ static void rb_tinytds_result_cancel_helper(DBPROCESS *client) {
99
+ GET_CLIENT_USERDATA(client);
100
+ rb_tinytds_result_ok_helper(client);
101
+ dbcancel(client);
102
+ userdata->dbcancel_sent = 1;
103
+ userdata->dbsql_sent = 0;
104
+ }
105
+
106
+ static VALUE rb_tinytds_result_fetch_row(VALUE self, ID timezone, int symbolize_keys, int as_array) {
107
+ /* Wrapper And Local Vars */
108
+ GET_RESULT_WRAPPER(self);
109
+ /* Create Empty Row */
110
+ VALUE row = as_array ? rb_ary_new2(rwrap->number_of_fields) : rb_hash_new();
111
+ /* Storing Values */
112
+ unsigned int i = 0;
113
+ for (i = 0; i < rwrap->number_of_fields; i++) {
114
+ VALUE val = Qnil;
115
+ int col = i+1;
116
+ int coltype = dbcoltype(rwrap->client, col);
117
+ BYTE *data = dbdata(rwrap->client, col);
118
+ DBINT data_len = dbdatlen(rwrap->client, col);
119
+ int null_val = ((data == NULL) && (data_len == 0));
120
+ if (!null_val) {
121
+ switch(coltype) {
122
+ case SYBINT1:
123
+ val = INT2FIX(*(DBTINYINT *)data);
124
+ break;
125
+ case SYBINT2:
126
+ val = INT2FIX(*(DBSMALLINT *)data);
127
+ break;
128
+ case SYBINT4:
129
+ val = INT2NUM(*(DBINT *)data);
130
+ break;
131
+ case SYBINT8:
132
+ val = LL2NUM(*(DBBIGINT *)data);
133
+ break;
134
+ case SYBBIT:
135
+ val = *(int *)data ? Qtrue : Qfalse;
136
+ break;
137
+ case SYBNUMERIC:
138
+ case SYBDECIMAL: {
139
+ DBTYPEINFO *data_info = dbcoltypeinfo(rwrap->client, col);
140
+ int data_slength = (int)data_info->precision + (int)data_info->scale + 1;
141
+ char converted_decimal[data_slength];
142
+ dbconvert(rwrap->client, coltype, data, data_len, SYBVARCHAR, (BYTE *)converted_decimal, -1);
143
+ val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new2((char *)converted_decimal));
144
+ break;
145
+ }
146
+ case SYBFLT8: {
147
+ double col_to_double = *(double *)data;
148
+ val = (col_to_double == 0.000000) ? opt_float_zero : rb_float_new(col_to_double);
149
+ break;
150
+ }
151
+ case SYBREAL: {
152
+ float col_to_float = *(float *)data;
153
+ val = (col_to_float == 0.0) ? opt_float_zero : rb_float_new(col_to_float);
154
+ break;
155
+ }
156
+ case SYBMONEY: {
157
+ DBMONEY *money = (DBMONEY *)data;
158
+ char converted_money[25];
159
+ long long money_value = ((long long)money->mnyhigh << 32) | money->mnylow;
160
+ sprintf(converted_money, "%lld", money_value);
161
+ val = rb_funcall(cBigDecimal, intern_new, 2, rb_str_new2(converted_money), opt_four);
162
+ val = rb_funcall(val, intern_divide, 1, opt_tenk);
163
+ break;
164
+ }
165
+ case SYBMONEY4: {
166
+ DBMONEY4 *money = (DBMONEY4 *)data;
167
+ char converted_money[20];
168
+ sprintf(converted_money, "%f", money->mny4 / 10000.0);
169
+ val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new2(converted_money));
170
+ break;
171
+ }
172
+ case SYBBINARY:
173
+ case SYBIMAGE:
174
+ val = rb_str_new((char *)data, (long)data_len);
175
+ #ifdef HAVE_RUBY_ENCODING_H
176
+ rb_enc_associate(val, binaryEncoding);
177
+ #endif
178
+ break;
179
+ case 36: { // SYBUNIQUE
180
+ char converted_unique[37];
181
+ dbconvert(rwrap->client, coltype, data, 37, SYBVARCHAR, (BYTE *)converted_unique, -1);
182
+ val = ENCODED_STR_NEW2(converted_unique);
183
+ break;
184
+ }
185
+ case SYBDATETIME4: {
186
+ DBDATETIME new_data;
187
+ dbconvert(rwrap->client, coltype, data, data_len, SYBDATETIME, (BYTE *)&new_data, sizeof(new_data));
188
+ data = (BYTE *)&new_data;
189
+ data_len = sizeof(new_data);
190
+ }
191
+ case SYBDATETIME: {
192
+ DBDATEREC date_rec;
193
+ dbdatecrack(rwrap->client, &date_rec, (DBDATETIME *)data);
194
+ int year = date_rec.dateyear,
195
+ month = date_rec.datemonth+1,
196
+ day = date_rec.datedmonth,
197
+ hour = date_rec.datehour,
198
+ min = date_rec.dateminute,
199
+ sec = date_rec.datesecond,
200
+ msec = date_rec.datemsecond;
201
+ if (year+month+day+hour+min+sec+msec != 0) {
202
+ VALUE offset = (timezone == intern_local) ? rwrap->local_offset : opt_zero;
203
+ /* Use DateTime */
204
+ if (year < 1902 || year+month+day > 2058) {
205
+ VALUE datetime_sec = INT2NUM(sec);
206
+ if (msec != 0) {
207
+ if ((opt_ruby_186 == 1 && sec < 59) || (opt_ruby_186 != 1)) {
208
+ #ifdef HAVE_RUBY_ENCODING_H
209
+ VALUE rational_msec = rb_Rational2(INT2NUM(msec*1000), opt_onemil);
210
+ #else
211
+ VALUE rational_msec = rb_funcall(rb_cObject, intern_Rational, 2, INT2NUM(msec*1000), opt_onemil);
212
+ #endif
213
+ datetime_sec = rb_funcall(datetime_sec, intern_plus, 1, rational_msec);
214
+ }
215
+ }
216
+ val = rb_funcall(cDateTime, intern_civil, 7, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), datetime_sec, offset);
217
+ val = rb_funcall(val, intern_new_offset, 1, offset);
218
+ /* Use Time */
219
+ } else {
220
+ val = rb_funcall(rb_cTime, timezone, 7, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec), INT2NUM(msec*1000));
221
+ }
222
+ }
223
+ break;
224
+ }
225
+ case SYBCHAR:
226
+ case SYBTEXT:
227
+ val = ENCODED_STR_NEW(data, data_len);
228
+ break;
229
+ default:
230
+ val = ENCODED_STR_NEW(data, data_len);
231
+ break;
232
+ }
233
+ }
234
+ if (as_array) {
235
+ rb_ary_store(row, i, val);
236
+ } else {
237
+ VALUE key;
238
+ if (rwrap->number_of_results == 0) {
239
+ key = rb_ary_entry(rwrap->fields, i);
240
+ } else {
241
+ key = rb_ary_entry(rb_ary_entry(rwrap->fields, rwrap->number_of_results), i);
242
+ }
243
+ rb_hash_aset(row, key, val);
244
+ }
245
+ }
246
+ return row;
247
+ }
248
+
249
+
250
+ // TinyTds::Client (public)
251
+
252
+ static VALUE rb_tinytds_result_fields(VALUE self) {
253
+ GET_RESULT_WRAPPER(self);
254
+ RETCODE dbsqlok_rc = rb_tinytds_result_ok_helper(rwrap->client);
255
+ RETCODE dbresults_rc = rb_tinytds_result_dbresults_retcode(self);
256
+ VALUE fields_processed = rb_ary_entry(rwrap->fields_processed, rwrap->number_of_results);
257
+ if ((dbsqlok_rc == SUCCEED) && (dbresults_rc == SUCCEED) && (fields_processed == Qnil)) {
258
+ /* Default query options. */
259
+ VALUE qopts = rb_iv_get(self, "@query_options");
260
+ int symbolize_keys = (rb_hash_aref(qopts, sym_symbolize_keys) == Qtrue) ? 1 : 0;
261
+ /* Set number_of_fields count for this result set. */
262
+ rwrap->number_of_fields = dbnumcols(rwrap->client);
263
+ if (rwrap->number_of_fields > 0) {
264
+ /* Create fields for this result set. */
265
+ unsigned int fldi = 0;
266
+ VALUE fields = rb_ary_new2(rwrap->number_of_fields);
267
+ for (fldi = 0; fldi < rwrap->number_of_fields; fldi++) {
268
+ char *colname = dbcolname(rwrap->client, fldi+1);
269
+ VALUE field = symbolize_keys ? ID2SYM(rb_intern(colname)) : rb_obj_freeze(ENCODED_STR_NEW2(colname));
270
+ rb_ary_store(fields, fldi, field);
271
+ }
272
+ /* Store the fields. */
273
+ if (rwrap->number_of_results == 0) {
274
+ rwrap->fields = fields;
275
+ } else if (rwrap->number_of_results == 1) {
276
+ VALUE multi_rs_fields = rb_ary_new();
277
+ rb_ary_store(multi_rs_fields, 0, rwrap->fields);
278
+ rb_ary_store(multi_rs_fields, 1, fields);
279
+ rwrap->fields = multi_rs_fields;
280
+ } else {
281
+ rb_ary_store(rwrap->fields, rwrap->number_of_results, fields);
282
+ }
283
+ }
284
+ rb_ary_store(rwrap->fields_processed, rwrap->number_of_results, Qtrue);
285
+ }
286
+ return rwrap->fields;
287
+ }
288
+
289
+ static VALUE rb_tinytds_result_each(int argc, VALUE * argv, VALUE self) {
290
+ GET_RESULT_WRAPPER(self);
291
+ GET_CLIENT_USERDATA(rwrap->client);
292
+ /* Local Vars */
293
+ VALUE qopts, opts, block;
294
+ ID timezone;
295
+ int symbolize_keys = 0, as_array = 0, cache_rows = 0, first = 0;
296
+ /* Merge Options Hash To Query Options. Populate Opts & Block Var. */
297
+ qopts = rb_iv_get(self, "@query_options");
298
+ if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1)
299
+ qopts = rb_funcall(qopts, intern_merge, 1, opts);
300
+ rb_iv_set(self, "@query_options", qopts);
301
+ /* Locals From Options */
302
+ if (rb_hash_aref(qopts, sym_first) == Qtrue)
303
+ first = 1;
304
+ if (rb_hash_aref(qopts, sym_symbolize_keys) == Qtrue)
305
+ symbolize_keys = 1;
306
+ if (rb_hash_aref(qopts, sym_as) == sym_array)
307
+ as_array = 1;
308
+ if (rb_hash_aref(qopts, sym_cache_rows) == Qtrue)
309
+ cache_rows = 1;
310
+ if (rb_hash_aref(qopts, sym_timezone) == sym_local) {
311
+ timezone = intern_local;
312
+ } else if (rb_hash_aref(qopts, sym_timezone) == sym_utc) {
313
+ timezone = intern_utc;
314
+ } else {
315
+ rb_warn(":timezone option must be :utc or :local - defaulting to :local");
316
+ timezone = intern_local;
317
+ }
318
+ /* Make The Results Or Yield Existing */
319
+ if (NIL_P(rwrap->results)) {
320
+ rwrap->results = rb_ary_new();
321
+ RETCODE dbsqlok_rc = rb_tinytds_result_ok_helper(rwrap->client);
322
+ RETCODE dbresults_rc = rb_tinytds_result_dbresults_retcode(self);
323
+ while ((dbsqlok_rc == SUCCEED) && (dbresults_rc == SUCCEED)) {
324
+ int has_rows = (DBROWS(rwrap->client) == SUCCEED) ? 1 : 0;
325
+ rb_tinytds_result_fields(self);
326
+ if (has_rows && rwrap->number_of_fields > 0) {
327
+ /* Create rows for this result set. */
328
+ unsigned long rowi = 0;
329
+ VALUE result = rb_ary_new();
330
+ while (dbnextrow(rwrap->client) != NO_MORE_ROWS) {
331
+ VALUE row = rb_tinytds_result_fetch_row(self, timezone, symbolize_keys, as_array);
332
+ if (cache_rows)
333
+ rb_ary_store(result, rowi, row);
334
+ if (!NIL_P(block))
335
+ rb_yield(row);
336
+ if (first) {
337
+ dbcanquery(rwrap->client);
338
+ userdata->dbcancel_sent = 1;
339
+ }
340
+ rowi++;
341
+ }
342
+ rwrap->number_of_rows = rowi;
343
+ /* Store the result. */
344
+ if (cache_rows) {
345
+ if (rwrap->number_of_results == 0) {
346
+ rwrap->results = result;
347
+ } else if (rwrap->number_of_results == 1) {
348
+ VALUE multi_resultsets = rb_ary_new();
349
+ rb_ary_store(multi_resultsets, 0, rwrap->results);
350
+ rb_ary_store(multi_resultsets, 1, result);
351
+ rwrap->results = multi_resultsets;
352
+ } else {
353
+ rb_ary_store(rwrap->results, rwrap->number_of_results, result);
354
+ }
355
+ }
356
+ // If we find results increment the counter that helpers use and setup the next loop.
357
+ rwrap->number_of_results = rwrap->number_of_results + 1;
358
+ dbresults_rc = rb_tinytds_result_dbresults_retcode(self);
359
+ } else {
360
+ // If we do not find results, side step the rb_tinytds_result_dbresults_retcode helper and
361
+ // manually populate its memoized array while nullifing any memoized fields too before loop.
362
+ dbresults_rc = dbresults(rwrap->client);
363
+ rb_ary_store(rwrap->dbresults_retcodes, rwrap->number_of_results, INT2FIX(dbresults_rc));
364
+ rb_ary_store(rwrap->fields_processed, rwrap->number_of_results, Qnil);
365
+ }
366
+ }
367
+ if (dbresults_rc == FAIL)
368
+ rb_warn("TinyTDS: Something in the dbresults() while loop set the return code to FAIL.\n");
369
+ userdata->dbsql_sent = 0;
370
+ } else if (!NIL_P(block)) {
371
+ unsigned long i;
372
+ for (i = 0; i < rwrap->number_of_rows; i++) {
373
+ rb_yield(rb_ary_entry(rwrap->results, i));
374
+ }
375
+ }
376
+ return rwrap->results;
377
+ }
378
+
379
+ static VALUE rb_tinytds_result_cancel(VALUE self) {
380
+ GET_RESULT_WRAPPER(self);
381
+ GET_CLIENT_USERDATA(rwrap->client);
382
+ if (rwrap->client && !userdata->dbcancel_sent)
383
+ rb_tinytds_result_cancel_helper(rwrap->client);
384
+ return Qtrue;
385
+ }
386
+
387
+ static VALUE rb_tinytds_result_do(VALUE self) {
388
+ GET_RESULT_WRAPPER(self);
389
+ if (rwrap->client) {
390
+ rb_tinytds_result_cancel_helper(rwrap->client);
391
+ return LONG2NUM((long)dbcount(rwrap->client));
392
+ } else {
393
+ return Qnil;
394
+ }
395
+ }
396
+
397
+ static VALUE rb_tinytds_result_affected_rows(VALUE self) {
398
+ GET_RESULT_WRAPPER(self);
399
+ if (rwrap->client) {
400
+ return LONG2NUM((long)dbcount(rwrap->client));
401
+ } else {
402
+ return Qnil;
403
+ }
404
+ }
405
+
406
+ /* Duplicated in client.c */
407
+ static VALUE rb_tinytds_result_return_code(VALUE self) {
408
+ GET_RESULT_WRAPPER(self);
409
+ if (rwrap->client && dbhasretstat(rwrap->client)) {
410
+ return LONG2NUM((long)dbretstatus(rwrap->client));
411
+ } else {
412
+ return Qnil;
413
+ }
414
+ }
415
+
416
+ static VALUE rb_tinytds_result_insert(VALUE self) {
417
+ GET_RESULT_WRAPPER(self);
418
+ if (rwrap->client) {
419
+ rb_tinytds_result_cancel_helper(rwrap->client);
420
+ VALUE identity = Qnil;
421
+ dbcmd(rwrap->client, "SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident");
422
+ if (dbsqlexec(rwrap->client) != FAIL && dbresults(rwrap->client) != FAIL && DBROWS(rwrap->client) != FAIL) {
423
+ while (dbnextrow(rwrap->client) != NO_MORE_ROWS) {
424
+ int col = 1;
425
+ BYTE *data = dbdata(rwrap->client, col);
426
+ DBINT data_len = dbdatlen(rwrap->client, col);
427
+ int null_val = ((data == NULL) && (data_len == 0));
428
+ if (!null_val)
429
+ identity = LONG2NUM(*(long *)data);
430
+ }
431
+ }
432
+ return identity;
433
+ } else {
434
+ return Qnil;
435
+ }
436
+ }
437
+
438
+
439
+ // Lib Init
440
+
441
+ void init_tinytds_result() {
442
+ /* Data Classes */
443
+ cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
444
+ cDate = rb_const_get(rb_cObject, rb_intern("Date"));
445
+ cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
446
+ /* Define TinyTds::Result */
447
+ cTinyTdsResult = rb_define_class_under(mTinyTds, "Result", rb_cObject);
448
+ /* Define TinyTds::Result Public Methods */
449
+ rb_define_method(cTinyTdsResult, "fields", rb_tinytds_result_fields, 0);
450
+ rb_define_method(cTinyTdsResult, "each", rb_tinytds_result_each, -1);
451
+ rb_define_method(cTinyTdsResult, "cancel", rb_tinytds_result_cancel, 0);
452
+ rb_define_method(cTinyTdsResult, "do", rb_tinytds_result_do, 0);
453
+ rb_define_method(cTinyTdsResult, "affected_rows", rb_tinytds_result_affected_rows, 0);
454
+ rb_define_method(cTinyTdsResult, "return_code", rb_tinytds_result_return_code, 0);
455
+ rb_define_method(cTinyTdsResult, "insert", rb_tinytds_result_insert, 0);
456
+ /* Intern String Helpers */
457
+ intern_new = rb_intern("new");
458
+ intern_utc = rb_intern("utc");
459
+ intern_local = rb_intern("local");
460
+ intern_merge = rb_intern("merge");
461
+ intern_localtime = rb_intern("localtime");
462
+ intern_civil = rb_intern("civil");
463
+ intern_new_offset = rb_intern("new_offset");
464
+ intern_plus = rb_intern("+");
465
+ intern_divide = rb_intern("/");
466
+ intern_Rational = rb_intern("Rational");
467
+ /* Symbol Helpers */
468
+ sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
469
+ sym_as = ID2SYM(rb_intern("as"));
470
+ sym_array = ID2SYM(rb_intern("array"));
471
+ sym_cache_rows = ID2SYM(rb_intern("cache_rows"));
472
+ sym_first = ID2SYM(rb_intern("first"));
473
+ sym_local = ID2SYM(intern_local);
474
+ sym_utc = ID2SYM(intern_utc);
475
+ sym_timezone = ID2SYM(rb_intern("timezone"));
476
+ /* Data Conversion Options */
477
+ opt_decimal_zero = rb_str_new2("0.0");
478
+ rb_global_variable(&opt_decimal_zero);
479
+ opt_float_zero = rb_float_new((double)0);
480
+ rb_global_variable(&opt_float_zero);
481
+ opt_one = INT2NUM(1);
482
+ opt_zero = INT2NUM(0);
483
+ opt_four = INT2NUM(4);
484
+ opt_19hdr = INT2NUM(1900);
485
+ opt_tenk = INT2NUM(10000);
486
+ opt_onemil = INT2NUM(1000000);
487
+ /* Ruby version flags */
488
+ opt_ruby_186 = (rb_eval_string("RUBY_VERSION == '1.8.6'") == Qtrue) ? 1 : 0;
489
+ /* Encoding */
490
+ #ifdef HAVE_RUBY_ENCODING_H
491
+ binaryEncoding = rb_enc_find("binary");
492
+ #endif
493
+ }
@@ -0,0 +1,36 @@
1
+
2
+ #ifndef TINYTDS_RESULT_H
3
+ #define TINYTDS_RESULT_H
4
+
5
+ // TODO: Is this needed?
6
+ typedef tds_sysdep_int64_type DBBIGINT; /* Missing in sybdb.h ?!?! */
7
+
8
+ void init_tinytds_result();
9
+ VALUE rb_tinytds_new_result_obj(DBPROCESS *c);
10
+
11
+ typedef struct {
12
+ DBPROCESS *client;
13
+ VALUE local_offset;
14
+ VALUE fields;
15
+ VALUE fields_processed;
16
+ VALUE results;
17
+ #ifdef HAVE_RUBY_ENCODING_H
18
+ rb_encoding *encoding;
19
+ #endif
20
+ VALUE dbresults_retcodes;
21
+ unsigned int number_of_results;
22
+ unsigned int number_of_fields;
23
+ unsigned long number_of_rows;
24
+ } tinytds_result_wrapper;
25
+
26
+
27
+ // Lib Macros
28
+
29
+ #define GET_RESULT_WRAPPER(self) \
30
+ tinytds_result_wrapper *rwrap; \
31
+ Data_Get_Struct(self, tinytds_result_wrapper, rwrap)
32
+
33
+
34
+
35
+
36
+ #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,18 @@
1
+ #ifndef TINYTDS_EXT
2
+ #define TINYTDS_EXT
3
+
4
+ #undef MSDBLIB
5
+ #define SYBDBLIB
6
+
7
+ #include <ruby.h>
8
+ #include <sybfront.h>
9
+ #include <sybdb.h>
10
+
11
+ #ifdef HAVE_RUBY_ENCODING_H
12
+ #include <ruby/encoding.h>
13
+ #endif
14
+
15
+ #include <client.h>
16
+ #include <result.h>
17
+
18
+ #endif
Binary file
Binary file
@@ -0,0 +1,79 @@
1
+ module TinyTds
2
+ class Client
3
+
4
+ TDS_VERSIONS_SETTERS = {
5
+ 'unknown' => 0,
6
+ '46' => 1,
7
+ '100' => 2,
8
+ '42' => 3,
9
+ '70' => 4,
10
+ '80' => 5,
11
+ '90' => 6 # TODO: untested
12
+ }.freeze
13
+
14
+ TDS_VERSIONS_GETTERS = {
15
+ 0 => {:name => 'DBTDS_UNKNOWN', :description => 'Unknown'},
16
+ 1 => {:name => 'DBTDS_2_0', :description => 'Pre 4.0 SQL Server'},
17
+ 2 => {:name => 'DBTDS_3_4', :description => 'Microsoft SQL Server (3.0)'},
18
+ 3 => {:name => 'DBTDS_4_0', :description => '4.0 SQL Server'},
19
+ 4 => {:name => 'DBTDS_4_2', :description => '4.2 SQL Server'},
20
+ 5 => {:name => 'DBTDS_4_6', :description => '2.0 OpenServer and 4.6 SQL Server.'},
21
+ 6 => {:name => 'DBTDS_4_9_5', :description => '4.9.5 (NCR) SQL Server'},
22
+ 7 => {:name => 'DBTDS_5_0', :description => '5.0 SQL Server'},
23
+ 8 => {:name => 'DBTDS_7_0', :description => 'Microsoft SQL Server 7.0'},
24
+ 9 => {:name => 'DBTDS_8_0', :description => 'Microsoft SQL Server 2000'},
25
+ 10 => {:name => 'DBTDS_9_0', :description => 'Microsoft SQL Server 2005'}
26
+ }.freeze
27
+
28
+ @@default_query_options = {
29
+ :as => :hash,
30
+ :symbolize_keys => false,
31
+ :cache_rows => true,
32
+ :timezone => :local
33
+ }
34
+
35
+ attr_reader :query_options
36
+
37
+ def self.default_query_options
38
+ @@default_query_options
39
+ end
40
+
41
+ # Most, if not all, iconv encoding names can be found by ruby. Just in case, you can
42
+ # overide this method to return a string name that Encoding.find would work with. Default
43
+ # is to return the passed encoding.
44
+ def self.transpose_iconv_encoding(encoding)
45
+ encoding
46
+ end
47
+
48
+
49
+ def initialize(opts={})
50
+ raise ArgumentError, 'missing :username option' if opts[:username].to_s.empty?
51
+ raise ArgumentError, 'missing :host option if no :dataserver given' if opts[:dataserver].to_s.empty? && opts[:host].to_s.empty?
52
+ @query_options = @@default_query_options.dup
53
+ opts[:appname] ||= 'TinyTds'
54
+ opts[:tds_version] = TDS_VERSIONS_SETTERS[opts[:tds_version].to_s] || TDS_VERSIONS_SETTERS['80']
55
+ opts[:login_timeout] ||= 60
56
+ opts[:timeout] ||= 5
57
+ opts[:encoding] = (opts[:encoding].nil? || opts[:encoding].downcase == 'utf8') ? 'UTF-8' : opts[:encoding].upcase
58
+ opts[:port] ||= 1433
59
+ opts[:dataserver] = "#{opts[:host]}:#{opts[:port]}" if opts[:dataserver].to_s.empty?
60
+ connect(opts)
61
+ end
62
+
63
+ def tds_version_info
64
+ info = TDS_VERSIONS_GETTERS[tds_version]
65
+ "#{info[:name]} - #{info[:description]}" if info
66
+ end
67
+
68
+ def active?
69
+ !closed? && !dead?
70
+ end
71
+
72
+ private
73
+
74
+ def self.local_offset
75
+ ::Time.local(2010).utc_offset.to_r / 86400
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,29 @@
1
+ module TinyTds
2
+ class Error < StandardError
3
+
4
+ SEVERITIES = [
5
+ {:number => 1, :severity => 'EXINFO', :explanation => 'Informational, non-error.'},
6
+ {:number => 2, :severity => 'EXUSER', :explanation => 'User error.'},
7
+ {:number => 3, :severity => 'EXNONFATAL', :explanation => 'Non-fatal error.'},
8
+ {:number => 4, :severity => 'EXCONVERSION', :explanation => 'Error in DB-Library data conversion.'},
9
+ {:number => 5, :severity => 'EXSERVER', :explanation => 'The Server has returned an error flag.'},
10
+ {:number => 6, :severity => 'EXTIME', :explanation => 'We have exceeded our timeout period while waiting for a response from the Server - the DBPROCESS is still alive.'},
11
+ {:number => 7, :severity => 'EXPROGRAM', :explanation => 'Coding error in user program.'},
12
+ {:number => 8, :severity => 'EXRESOURCE', :explanation => 'Running out of resources - the DBPROCESS may be dead.'},
13
+ {:number => 9, :severity => 'EXCOMM', :explanation => 'Failure in communication with Server - the DBPROCESS is dead.'},
14
+ {:number => 10, :severity => 'EXFATAL', :explanation => 'Fatal error - the DBPROCESS is dead.'},
15
+ {:number => 11, :severity => 'EXCONSISTENCY', :explanation => 'Internal software error - notify Sybase Technical Support.'}
16
+ ].freeze
17
+
18
+ attr_accessor :source, :severity, :db_error_number, :os_error_number
19
+
20
+ def initialize(message)
21
+ super
22
+ @severity = nil
23
+ @db_error_number = nil
24
+ @os_error_number = nil
25
+ end
26
+
27
+
28
+ end
29
+ end
@@ -0,0 +1,8 @@
1
+ module TinyTds
2
+ class Result
3
+
4
+ include Enumerable
5
+
6
+
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ module TinyTds
2
+ VERSION = '0.4.5.rc1'
3
+ end