tiny_tds 0.4.5.rc1-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
@@ -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