tiny_tds 0.6.2-x64-mingw32

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,46 @@
1
+
2
+ #ifndef TINYTDS_CLIENT_H
3
+ #define TINYTDS_CLIENT_H
4
+
5
+ void init_tinytds_client();
6
+
7
+ typedef struct {
8
+ short int is_set;
9
+ int cancel;
10
+ char error[1024];
11
+ char source[1024];
12
+ int severity;
13
+ int dberr;
14
+ int oserr;
15
+ } tinytds_errordata;
16
+
17
+ typedef struct {
18
+ short int closed;
19
+ short int timing_out;
20
+ short int dbsql_sent;
21
+ short int dbsqlok_sent;
22
+ RETCODE dbsqlok_retcode;
23
+ short int dbcancel_sent;
24
+ short int nonblocking;
25
+ tinytds_errordata nonblocking_error;
26
+ } tinytds_client_userdata;
27
+
28
+ typedef struct {
29
+ LOGINREC *login;
30
+ RETCODE return_code;
31
+ DBPROCESS *client;
32
+ short int closed;
33
+ VALUE charset;
34
+ tinytds_client_userdata *userdata;
35
+ const char *identity_insert_sql;
36
+ rb_encoding *encoding;
37
+ } tinytds_client_wrapper;
38
+
39
+
40
+ // Lib Macros
41
+
42
+ #define GET_CLIENT_USERDATA(dbproc) \
43
+ tinytds_client_userdata *userdata = (tinytds_client_userdata *)dbgetuserdata(dbproc);
44
+
45
+
46
+ #endif
@@ -0,0 +1,102 @@
1
+ ENV['RC_ARCHS'] = '' if RUBY_PLATFORM =~ /darwin/
2
+
3
+ # :stopdoc:
4
+
5
+ require 'mkmf'
6
+
7
+ # Shamelessly copied from nokogiri
8
+ #
9
+
10
+ FREETDSDIR = ENV['FREETDS_DIR']
11
+
12
+ if FREETDSDIR.nil? || FREETDSDIR.empty?
13
+ LIBDIR = RbConfig::CONFIG['libdir']
14
+ INCLUDEDIR = RbConfig::CONFIG['includedir']
15
+ else
16
+ puts "Will use #{FREETDSDIR}"
17
+ LIBDIR = "#{FREETDSDIR}/lib"
18
+ INCLUDEDIR = "#{FREETDSDIR}/include"
19
+ end
20
+
21
+ $CFLAGS << " #{ENV["CFLAGS"]}"
22
+ $LDFLAGS << " #{ENV["LDFLAGS"]}"
23
+ $LIBS << " #{ENV["LIBS"]}"
24
+
25
+ SEARCHABLE_PATHS = begin
26
+ eop_regexp = /#{File::SEPARATOR}bin$/
27
+ paths = ENV['PATH']
28
+ paths = paths.gsub(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
29
+ paths = paths.split(File::PATH_SEPARATOR)
30
+ bin_paths = paths.select{ |p| p =~ eop_regexp }
31
+ bin_paths.map{ |p| p.sub(eop_regexp,'') }.compact.reject{ |p| p.empty? }.uniq
32
+ end
33
+
34
+ def searchable_paths_with_directories(*directories)
35
+ SEARCHABLE_PATHS.map do |path|
36
+ directories.map do |paths|
37
+ dir = File.join path, *paths
38
+ File.directory?(dir) ? dir : nil
39
+ end.flatten.compact
40
+ end.flatten.compact
41
+ end
42
+
43
+ if RbConfig::CONFIG['target_os'] =~ /mswin32|mingw32/
44
+ lib_prefix = 'lib' unless RbConfig::CONFIG['target_os'] =~ /mingw32/
45
+ # There's no default include/lib dir on Windows. Let's just add the Ruby ones
46
+ # and resort on the search path specified by INCLUDE and LIB environment
47
+ # variables
48
+ HEADER_DIRS = [INCLUDEDIR]
49
+ LIB_DIRS = [LIBDIR]
50
+ else
51
+ lib_prefix = ''
52
+ HEADER_DIRS = [
53
+ # First search /opt/local for macports
54
+ '/opt/local/include',
55
+ # Then search /usr/local for people that installed from source
56
+ '/usr/local/include',
57
+ # Check the ruby install locations
58
+ INCLUDEDIR,
59
+ # Finally fall back to /usr
60
+ '/usr/include'
61
+ ].reject{ |dir| !File.directory?(dir) }
62
+ LIB_DIRS = [
63
+ # First search /opt/local for macports
64
+ '/opt/local/lib',
65
+ # Then search /usr/local for people that installed from source
66
+ '/usr/local/lib',
67
+ # Check the ruby install locations
68
+ LIBDIR,
69
+ # Finally fall back to /usr
70
+ '/usr/lib',
71
+ ].reject{ |dir| !File.directory?(dir) }
72
+ end
73
+
74
+ FREETDS_HEADER_DIRS = (searchable_paths_with_directories(['include'],['include','freetds']) + HEADER_DIRS).uniq
75
+ FREETDS_LIB_DIRS = (searchable_paths_with_directories(['lib'],['lib','freetds']) + LIB_DIRS).uniq
76
+
77
+ # lookup over searchable paths is great for native compilation, however, when
78
+ # cross compiling we need to specify our own paths.
79
+ if enable_config("lookup", true)
80
+ dir_config('iconv', FREETDS_HEADER_DIRS, FREETDS_LIB_DIRS)
81
+ dir_config('freetds', FREETDS_HEADER_DIRS, FREETDS_LIB_DIRS)
82
+ else
83
+ dir_config('iconv')
84
+ dir_config('freetds')
85
+
86
+ # remove LDFLAGS
87
+ $LDFLAGS = ENV.fetch("LDFLAGS", "")
88
+ end
89
+
90
+ def asplode(lib)
91
+ abort "-----\n#{lib} is missing.\n-----"
92
+ end
93
+
94
+ asplode 'libiconv' unless have_func('iconv_open', 'iconv.h') || have_library('iconv', 'iconv_open', 'iconv.h')
95
+ asplode 'freetds' unless have_header('sybfront.h') && have_header('sybdb.h')
96
+
97
+ asplode 'freetds' unless find_library("#{lib_prefix}sybdb", 'tdsdbopen')
98
+ asplode 'freetds' unless find_library("#{lib_prefix}ct", 'ct_bind')
99
+
100
+ create_makefile('tiny_tds/tiny_tds')
101
+
102
+ # :startdoc:
@@ -0,0 +1,599 @@
1
+
2
+ #include <tiny_tds_ext.h>
3
+ #include <stdint.h>
4
+
5
+ // TINY_TDS_MAX_TIME
6
+
7
+ #define TINY_TDS_MAX_TIME 315607276799ULL
8
+
9
+
10
+ // TINY_TDS_MIN_TIME
11
+
12
+ #define TINY_TDS_MIN_TIME 2678400ULL
13
+
14
+
15
+ // File Types/Vars
16
+
17
+ VALUE cTinyTdsResult;
18
+ extern VALUE mTinyTds, cTinyTdsClient, cTinyTdsError;
19
+ VALUE cBigDecimal, cDate, cDateTime;
20
+ VALUE opt_decimal_zero, opt_float_zero, opt_one, opt_zero, opt_four, opt_19hdr, opt_tenk, opt_onemil;
21
+ int opt_ruby_186;
22
+ static ID intern_new, intern_utc, intern_local, intern_localtime, intern_merge,
23
+ intern_civil, intern_new_offset, intern_plus, intern_divide, intern_Rational;
24
+ static ID sym_symbolize_keys, sym_as, sym_array, sym_cache_rows, sym_first, sym_timezone, sym_local, sym_utc, sym_empty_sets;
25
+
26
+
27
+ // Lib Macros
28
+
29
+ rb_encoding *binaryEncoding;
30
+ #define ENCODED_STR_NEW(_data, _len) ({ \
31
+ VALUE _val = rb_str_new((char *)_data, (long)_len); \
32
+ rb_enc_associate(_val, rwrap->encoding); \
33
+ _val; \
34
+ })
35
+ #define ENCODED_STR_NEW2(_data2) ({ \
36
+ VALUE _val = rb_str_new2((char *)_data2); \
37
+ rb_enc_associate(_val, rwrap->encoding); \
38
+ _val; \
39
+ })
40
+
41
+
42
+ // Lib Backend (Memory Management)
43
+
44
+ static void rb_tinytds_result_mark(void *ptr) {
45
+ tinytds_result_wrapper *rwrap = (tinytds_result_wrapper *)ptr;
46
+ if (rwrap) {
47
+ rb_gc_mark(rwrap->local_offset);
48
+ rb_gc_mark(rwrap->fields);
49
+ rb_gc_mark(rwrap->fields_processed);
50
+ rb_gc_mark(rwrap->results);
51
+ rb_gc_mark(rwrap->dbresults_retcodes);
52
+ }
53
+ }
54
+
55
+ static void rb_tinytds_result_free(void *ptr) {
56
+ tinytds_result_wrapper *rwrap = (tinytds_result_wrapper *)ptr;
57
+ xfree(ptr);
58
+ }
59
+
60
+ VALUE rb_tinytds_new_result_obj(tinytds_client_wrapper *cwrap) {
61
+ VALUE obj;
62
+ tinytds_result_wrapper *rwrap;
63
+ obj = Data_Make_Struct(cTinyTdsResult, tinytds_result_wrapper, rb_tinytds_result_mark, rb_tinytds_result_free, rwrap);
64
+ rwrap->cwrap = cwrap;
65
+ rwrap->client = cwrap->client;
66
+ rwrap->local_offset = Qnil;
67
+ rwrap->fields = rb_ary_new();
68
+ rwrap->fields_processed = rb_ary_new();
69
+ rwrap->results = Qnil;
70
+ rwrap->dbresults_retcodes = rb_ary_new();
71
+ rwrap->number_of_results = 0;
72
+ rwrap->number_of_fields = 0;
73
+ rwrap->number_of_rows = 0;
74
+ rb_obj_call_init(obj, 0, NULL);
75
+ return obj;
76
+ }
77
+
78
+ // No GVL Helpers
79
+
80
+ #define NOGVL_DBCALL(_dbfunction, _client) ( \
81
+ (RETCODE)rb_thread_blocking_region( \
82
+ (rb_blocking_function_t*)_dbfunction, _client, \
83
+ (rb_unblock_function_t*)dbcancel_ubf, _client ) \
84
+ )
85
+
86
+ static void dbcancel_ubf(DBPROCESS *client) {
87
+ GET_CLIENT_USERDATA(client);
88
+ dbcancel(client);
89
+ userdata->dbcancel_sent = 1;
90
+ }
91
+
92
+ static void nogvl_setup(DBPROCESS *client) {
93
+ GET_CLIENT_USERDATA(client);
94
+ userdata->nonblocking = 1;
95
+ }
96
+
97
+ static void nogvl_cleanup(DBPROCESS *client) {
98
+ GET_CLIENT_USERDATA(client);
99
+ userdata->nonblocking = 0;
100
+ /*
101
+ Now that the blocking operation is done, we can finally throw any
102
+ exceptions based on errors from SQL Server.
103
+ */
104
+ if (userdata->nonblocking_error.is_set) {
105
+ userdata->nonblocking_error.is_set = 0;
106
+ rb_tinytds_raise_error(client,
107
+ userdata->nonblocking_error.cancel,
108
+ &userdata->nonblocking_error.error,
109
+ &userdata->nonblocking_error.source,
110
+ userdata->nonblocking_error.severity,
111
+ userdata->nonblocking_error.dberr,
112
+ userdata->nonblocking_error.oserr);
113
+ }
114
+ }
115
+
116
+ static RETCODE nogvl_dbsqlok(DBPROCESS *client) {
117
+ int retcode = FAIL;
118
+ GET_CLIENT_USERDATA(client);
119
+ nogvl_setup(client);
120
+ retcode = NOGVL_DBCALL(dbsqlok, client);
121
+ nogvl_cleanup(client);
122
+ userdata->dbsqlok_sent = 1;
123
+ return retcode;
124
+ }
125
+
126
+ static RETCODE nogvl_dbsqlexec(DBPROCESS *client) {
127
+ int retcode = FAIL;
128
+ nogvl_setup(client);
129
+ retcode = NOGVL_DBCALL(dbsqlexec, client);
130
+ nogvl_cleanup(client);
131
+ return retcode;
132
+ }
133
+
134
+ static RETCODE nogvl_dbresults(DBPROCESS *client) {
135
+ int retcode = FAIL;
136
+ nogvl_setup(client);
137
+ retcode = NOGVL_DBCALL(dbresults, client);
138
+ nogvl_cleanup(client);
139
+ return retcode;
140
+ }
141
+
142
+ static RETCODE nogvl_dbnextrow(DBPROCESS * client) {
143
+ int retcode = FAIL;
144
+ nogvl_setup(client);
145
+ retcode = NOGVL_DBCALL(dbnextrow, client);
146
+ nogvl_cleanup(client);
147
+ return retcode;
148
+ }
149
+
150
+ // Lib Backend (Helpers)
151
+
152
+ static RETCODE rb_tinytds_result_dbresults_retcode(VALUE self) {
153
+ GET_RESULT_WRAPPER(self);
154
+ VALUE ruby_rc;
155
+ RETCODE db_rc;
156
+ ruby_rc = rb_ary_entry(rwrap->dbresults_retcodes, rwrap->number_of_results);
157
+ if (NIL_P(ruby_rc)) {
158
+ db_rc = nogvl_dbresults(rwrap->client);
159
+ ruby_rc = INT2FIX(db_rc);
160
+ rb_ary_store(rwrap->dbresults_retcodes, rwrap->number_of_results, ruby_rc);
161
+ } else {
162
+ db_rc = FIX2INT(ruby_rc);
163
+ }
164
+ return db_rc;
165
+ }
166
+
167
+ static RETCODE rb_tinytds_result_ok_helper(DBPROCESS *client) {
168
+ GET_CLIENT_USERDATA(client);
169
+ if (userdata->dbsqlok_sent == 0) {
170
+ userdata->dbsqlok_retcode = nogvl_dbsqlok(client);
171
+ }
172
+ return userdata->dbsqlok_retcode;
173
+ }
174
+
175
+ static void rb_tinytds_result_exec_helper(DBPROCESS *client) {
176
+ GET_CLIENT_USERDATA(client);
177
+ RETCODE dbsqlok_rc = rb_tinytds_result_ok_helper(client);
178
+ if (dbsqlok_rc == SUCCEED) {
179
+ /*
180
+ This is to just process each result set. Commands such as backup and
181
+ restore are not done when the first result set is returned, so we need to
182
+ exhaust the result sets before it is complete.
183
+ */
184
+ while (nogvl_dbresults(client) == SUCCEED) {
185
+ /*
186
+ If we don't loop through each row for calls to TinyTds::Result.do that
187
+ actually do return result sets, we will trigger error 20019 about trying
188
+ to execute a new command with pending results. Oh well.
189
+ */
190
+ while (dbnextrow(client) != NO_MORE_ROWS);
191
+ }
192
+ }
193
+ dbcancel(client);
194
+ userdata->dbcancel_sent = 1;
195
+ userdata->dbsql_sent = 0;
196
+ }
197
+
198
+ static VALUE rb_tinytds_result_fetch_row(VALUE self, ID timezone, int symbolize_keys, int as_array) {
199
+ /* Wrapper And Local Vars */
200
+ GET_RESULT_WRAPPER(self);
201
+ /* Create Empty Row */
202
+ VALUE row = as_array ? rb_ary_new2(rwrap->number_of_fields) : rb_hash_new();
203
+ /* Storing Values */
204
+ unsigned int i = 0;
205
+ for (i = 0; i < rwrap->number_of_fields; i++) {
206
+ VALUE val = Qnil;
207
+ int col = i+1;
208
+ int coltype = dbcoltype(rwrap->client, col);
209
+ BYTE *data = dbdata(rwrap->client, col);
210
+ DBINT data_len = dbdatlen(rwrap->client, col);
211
+ int null_val = ((data == NULL) && (data_len == 0));
212
+ if (!null_val) {
213
+ switch(coltype) {
214
+ case SYBINT1:
215
+ val = INT2FIX(*(DBTINYINT *)data);
216
+ break;
217
+ case SYBINT2:
218
+ val = INT2FIX(*(DBSMALLINT *)data);
219
+ break;
220
+ case SYBINT4:
221
+ val = INT2NUM(*(DBINT *)data);
222
+ break;
223
+ case SYBINT8:
224
+ val = LL2NUM(*(DBBIGINT *)data);
225
+ break;
226
+ case SYBBIT:
227
+ val = *(int *)data ? Qtrue : Qfalse;
228
+ break;
229
+ case SYBNUMERIC:
230
+ case SYBDECIMAL: {
231
+ DBTYPEINFO *data_info = dbcoltypeinfo(rwrap->client, col);
232
+ int data_slength = (int)data_info->precision + (int)data_info->scale + 1;
233
+ char converted_decimal[data_slength];
234
+ dbconvert(rwrap->client, coltype, data, data_len, SYBVARCHAR, (BYTE *)converted_decimal, -1);
235
+ val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new2((char *)converted_decimal));
236
+ break;
237
+ }
238
+ case SYBFLT8: {
239
+ double col_to_double = *(double *)data;
240
+ val = (col_to_double == 0.000000) ? opt_float_zero : rb_float_new(col_to_double);
241
+ break;
242
+ }
243
+ case SYBREAL: {
244
+ float col_to_float = *(float *)data;
245
+ val = (col_to_float == 0.0) ? opt_float_zero : rb_float_new(col_to_float);
246
+ break;
247
+ }
248
+ case SYBMONEY: {
249
+ DBMONEY *money = (DBMONEY *)data;
250
+ char converted_money[25];
251
+ long long money_value = ((long long)money->mnyhigh << 32) | money->mnylow;
252
+ sprintf(converted_money, "%lld", money_value);
253
+ val = rb_funcall(cBigDecimal, intern_new, 2, rb_str_new2(converted_money), opt_four);
254
+ val = rb_funcall(val, intern_divide, 1, opt_tenk);
255
+ break;
256
+ }
257
+ case SYBMONEY4: {
258
+ DBMONEY4 *money = (DBMONEY4 *)data;
259
+ char converted_money[20];
260
+ sprintf(converted_money, "%f", money->mny4 / 10000.0);
261
+ val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new2(converted_money));
262
+ break;
263
+ }
264
+ case SYBBINARY:
265
+ case SYBIMAGE:
266
+ val = rb_str_new((char *)data, (long)data_len);
267
+ #ifdef HAVE_RUBY_ENCODING_H
268
+ rb_enc_associate(val, binaryEncoding);
269
+ #endif
270
+ break;
271
+ case 36: { // SYBUNIQUE
272
+ char converted_unique[37];
273
+ dbconvert(rwrap->client, coltype, data, 37, SYBVARCHAR, (BYTE *)converted_unique, -1);
274
+ val = ENCODED_STR_NEW2(converted_unique);
275
+ break;
276
+ }
277
+ case SYBDATETIME4: {
278
+ DBDATETIME new_data;
279
+ dbconvert(rwrap->client, coltype, data, data_len, SYBDATETIME, (BYTE *)&new_data, sizeof(new_data));
280
+ data = (BYTE *)&new_data;
281
+ data_len = sizeof(new_data);
282
+ }
283
+ case SYBDATETIME: {
284
+ DBDATEREC date_rec;
285
+ dbdatecrack(rwrap->client, &date_rec, (DBDATETIME *)data);
286
+ int year = date_rec.dateyear,
287
+ month = date_rec.datemonth+1,
288
+ day = date_rec.datedmonth,
289
+ hour = date_rec.datehour,
290
+ min = date_rec.dateminute,
291
+ sec = date_rec.datesecond,
292
+ msec = date_rec.datemsecond;
293
+ if (year+month+day+hour+min+sec+msec != 0) {
294
+ VALUE offset = (timezone == intern_local) ? rwrap->local_offset : opt_zero;
295
+ uint64_t seconds = (year*31557600ULL) + (month*2592000ULL) + (day*86400ULL) + (hour*3600ULL) + (min*60ULL) + sec;
296
+ /* Use DateTime */
297
+ if (seconds < TINY_TDS_MIN_TIME || seconds > TINY_TDS_MAX_TIME) {
298
+ VALUE datetime_sec = INT2NUM(sec);
299
+ if (msec != 0) {
300
+ if ((opt_ruby_186 == 1 && sec < 59) || (opt_ruby_186 != 1)) {
301
+ #ifdef HAVE_RUBY_ENCODING_H
302
+ VALUE rational_msec = rb_Rational2(INT2NUM(msec*1000), opt_onemil);
303
+ #else
304
+ VALUE rational_msec = rb_funcall(rb_cObject, intern_Rational, 2, INT2NUM(msec*1000), opt_onemil);
305
+ #endif
306
+ datetime_sec = rb_funcall(datetime_sec, intern_plus, 1, rational_msec);
307
+ }
308
+ }
309
+ val = rb_funcall(cDateTime, intern_civil, 7, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), datetime_sec, offset);
310
+ val = rb_funcall(val, intern_new_offset, 1, offset);
311
+ /* Use Time */
312
+ } else {
313
+ val = rb_funcall(rb_cTime, timezone, 7, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec), INT2NUM(msec*1000));
314
+ }
315
+ }
316
+ break;
317
+ }
318
+ case SYBCHAR:
319
+ case SYBTEXT:
320
+ val = ENCODED_STR_NEW(data, data_len);
321
+ break;
322
+ default:
323
+ val = ENCODED_STR_NEW(data, data_len);
324
+ break;
325
+ }
326
+ }
327
+ if (as_array) {
328
+ rb_ary_store(row, i, val);
329
+ } else {
330
+ VALUE key;
331
+ if (rwrap->number_of_results == 0) {
332
+ key = rb_ary_entry(rwrap->fields, i);
333
+ } else {
334
+ key = rb_ary_entry(rb_ary_entry(rwrap->fields, rwrap->number_of_results), i);
335
+ }
336
+ rb_hash_aset(row, key, val);
337
+ }
338
+ }
339
+ return row;
340
+ }
341
+
342
+
343
+ // TinyTds::Client (public)
344
+
345
+ static VALUE rb_tinytds_result_fields(VALUE self) {
346
+ GET_RESULT_WRAPPER(self);
347
+ RETCODE dbsqlok_rc = rb_tinytds_result_ok_helper(rwrap->client);
348
+ RETCODE dbresults_rc = rb_tinytds_result_dbresults_retcode(self);
349
+ VALUE fields_processed = rb_ary_entry(rwrap->fields_processed, rwrap->number_of_results);
350
+ if ((dbsqlok_rc == SUCCEED) && (dbresults_rc == SUCCEED) && (fields_processed == Qnil)) {
351
+ /* Default query options. */
352
+ int symbolize_keys = 0;
353
+ VALUE qopts = rb_iv_get(self, "@query_options");
354
+ if (rb_hash_aref(qopts, sym_symbolize_keys) == Qtrue)
355
+ symbolize_keys = 1;
356
+ /* Set number_of_fields count for this result set. */
357
+ rwrap->number_of_fields = dbnumcols(rwrap->client);
358
+ if (rwrap->number_of_fields > 0) {
359
+ /* Create fields for this result set. */
360
+ unsigned int fldi = 0;
361
+ VALUE fields = rb_ary_new2(rwrap->number_of_fields);
362
+ for (fldi = 0; fldi < rwrap->number_of_fields; fldi++) {
363
+ char *colname = dbcolname(rwrap->client, fldi+1);
364
+ VALUE field = symbolize_keys ? rb_str_intern(ENCODED_STR_NEW2(colname)) : rb_obj_freeze(ENCODED_STR_NEW2(colname));
365
+ rb_ary_store(fields, fldi, field);
366
+ }
367
+ /* Store the fields. */
368
+ if (rwrap->number_of_results == 0) {
369
+ rwrap->fields = fields;
370
+ } else if (rwrap->number_of_results == 1) {
371
+ VALUE multi_rs_fields = rb_ary_new();
372
+ rb_ary_store(multi_rs_fields, 0, rwrap->fields);
373
+ rb_ary_store(multi_rs_fields, 1, fields);
374
+ rwrap->fields = multi_rs_fields;
375
+ } else {
376
+ rb_ary_store(rwrap->fields, rwrap->number_of_results, fields);
377
+ }
378
+ }
379
+ rb_ary_store(rwrap->fields_processed, rwrap->number_of_results, Qtrue);
380
+ }
381
+ return rwrap->fields;
382
+ }
383
+
384
+ static VALUE rb_tinytds_result_each(int argc, VALUE * argv, VALUE self) {
385
+ GET_RESULT_WRAPPER(self);
386
+ GET_CLIENT_USERDATA(rwrap->client);
387
+ /* Local Vars */
388
+ VALUE qopts, opts, block;
389
+ ID timezone;
390
+ int symbolize_keys = 0, as_array = 0, cache_rows = 0, first = 0, empty_sets = 0;
391
+ /* Merge Options Hash To Query Options. Populate Opts & Block Var. */
392
+ qopts = rb_iv_get(self, "@query_options");
393
+ if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1)
394
+ qopts = rb_funcall(qopts, intern_merge, 1, opts);
395
+ rb_iv_set(self, "@query_options", qopts);
396
+ /* Locals From Options */
397
+ if (rb_hash_aref(qopts, sym_first) == Qtrue)
398
+ first = 1;
399
+ if (rb_hash_aref(qopts, sym_symbolize_keys) == Qtrue)
400
+ symbolize_keys = 1;
401
+ if (rb_hash_aref(qopts, sym_as) == sym_array)
402
+ as_array = 1;
403
+ if (rb_hash_aref(qopts, sym_cache_rows) == Qtrue)
404
+ cache_rows = 1;
405
+ if (rb_hash_aref(qopts, sym_timezone) == sym_local) {
406
+ timezone = intern_local;
407
+ } else if (rb_hash_aref(qopts, sym_timezone) == sym_utc) {
408
+ timezone = intern_utc;
409
+ } else {
410
+ rb_warn(":timezone option must be :utc or :local - defaulting to :local");
411
+ timezone = intern_local;
412
+ }
413
+ if (rb_hash_aref(qopts, sym_empty_sets) == Qtrue)
414
+ empty_sets = 1;
415
+ /* Make The Results Or Yield Existing */
416
+ if (NIL_P(rwrap->results)) {
417
+ rwrap->results = rb_ary_new();
418
+ RETCODE dbsqlok_rc = rb_tinytds_result_ok_helper(rwrap->client);
419
+ RETCODE dbresults_rc = rb_tinytds_result_dbresults_retcode(self);
420
+ while ((dbsqlok_rc == SUCCEED) && (dbresults_rc == SUCCEED)) {
421
+ int has_rows = (DBROWS(rwrap->client) == SUCCEED) ? 1 : 0;
422
+ if (has_rows || empty_sets || (rwrap->number_of_results == 0))
423
+ rb_tinytds_result_fields(self);
424
+ if ((has_rows || empty_sets) && rwrap->number_of_fields > 0) {
425
+ /* Create rows for this result set. */
426
+ unsigned long rowi = 0;
427
+ VALUE result = rb_ary_new();
428
+ while (nogvl_dbnextrow(rwrap->client) != NO_MORE_ROWS) {
429
+ VALUE row = rb_tinytds_result_fetch_row(self, timezone, symbolize_keys, as_array);
430
+ if (cache_rows)
431
+ rb_ary_store(result, rowi, row);
432
+ if (!NIL_P(block))
433
+ rb_yield(row);
434
+ if (first) {
435
+ dbcanquery(rwrap->client);
436
+ userdata->dbcancel_sent = 1;
437
+ }
438
+ rowi++;
439
+ }
440
+ rwrap->number_of_rows = rowi;
441
+ /* Store the result. */
442
+ if (cache_rows) {
443
+ if (rwrap->number_of_results == 0) {
444
+ rwrap->results = result;
445
+ } else if (rwrap->number_of_results == 1) {
446
+ VALUE multi_resultsets = rb_ary_new();
447
+ rb_ary_store(multi_resultsets, 0, rwrap->results);
448
+ rb_ary_store(multi_resultsets, 1, result);
449
+ rwrap->results = multi_resultsets;
450
+ } else {
451
+ rb_ary_store(rwrap->results, rwrap->number_of_results, result);
452
+ }
453
+ }
454
+ // If we find results increment the counter that helpers use and setup the next loop.
455
+ rwrap->number_of_results = rwrap->number_of_results + 1;
456
+ dbresults_rc = rb_tinytds_result_dbresults_retcode(self);
457
+ rb_ary_store(rwrap->fields_processed, rwrap->number_of_results, Qnil);
458
+ } else {
459
+ // If we do not find results, side step the rb_tinytds_result_dbresults_retcode helper and
460
+ // manually populate its memoized array while nullifing any memoized fields too before loop.
461
+ dbresults_rc = nogvl_dbresults(rwrap->client);
462
+ rb_ary_store(rwrap->dbresults_retcodes, rwrap->number_of_results, INT2FIX(dbresults_rc));
463
+ rb_ary_store(rwrap->fields_processed, rwrap->number_of_results, Qnil);
464
+ }
465
+ }
466
+ if (dbresults_rc == FAIL)
467
+ rb_warn("TinyTDS: Something in the dbresults() while loop set the return code to FAIL.\n");
468
+ userdata->dbsql_sent = 0;
469
+ } else if (!NIL_P(block)) {
470
+ unsigned long i;
471
+ for (i = 0; i < rwrap->number_of_rows; i++) {
472
+ rb_yield(rb_ary_entry(rwrap->results, i));
473
+ }
474
+ }
475
+ return rwrap->results;
476
+ }
477
+
478
+ static VALUE rb_tinytds_result_cancel(VALUE self) {
479
+ GET_RESULT_WRAPPER(self);
480
+ GET_CLIENT_USERDATA(rwrap->client);
481
+ if (rwrap->client && !userdata->dbcancel_sent) {
482
+ RETCODE dbsqlok_rc = rb_tinytds_result_ok_helper(rwrap->client);
483
+ dbcancel(rwrap->client);
484
+ userdata->dbcancel_sent = 1;
485
+ userdata->dbsql_sent = 0;
486
+ }
487
+ return Qtrue;
488
+ }
489
+
490
+ static VALUE rb_tinytds_result_do(VALUE self) {
491
+ GET_RESULT_WRAPPER(self);
492
+ if (rwrap->client) {
493
+ rb_tinytds_result_exec_helper(rwrap->client);
494
+ return LONG2NUM((long)dbcount(rwrap->client));
495
+ } else {
496
+ return Qnil;
497
+ }
498
+ }
499
+
500
+ static VALUE rb_tinytds_result_affected_rows(VALUE self) {
501
+ GET_RESULT_WRAPPER(self);
502
+ if (rwrap->client) {
503
+ return LONG2NUM((long)dbcount(rwrap->client));
504
+ } else {
505
+ return Qnil;
506
+ }
507
+ }
508
+
509
+ /* Duplicated in client.c */
510
+ static VALUE rb_tinytds_result_return_code(VALUE self) {
511
+ GET_RESULT_WRAPPER(self);
512
+ if (rwrap->client && dbhasretstat(rwrap->client)) {
513
+ return LONG2NUM((long)dbretstatus(rwrap->client));
514
+ } else {
515
+ return Qnil;
516
+ }
517
+ }
518
+
519
+ static VALUE rb_tinytds_result_insert(VALUE self) {
520
+ GET_RESULT_WRAPPER(self);
521
+ if (rwrap->client) {
522
+ rb_tinytds_result_exec_helper(rwrap->client);
523
+ VALUE identity = Qnil;
524
+ dbcmd(rwrap->client, rwrap->cwrap->identity_insert_sql);
525
+ if (nogvl_dbsqlexec(rwrap->client) != FAIL
526
+ && nogvl_dbresults(rwrap->client) != FAIL
527
+ && DBROWS(rwrap->client) != FAIL) {
528
+ while (nogvl_dbnextrow(rwrap->client) != NO_MORE_ROWS) {
529
+ int col = 1;
530
+ BYTE *data = dbdata(rwrap->client, col);
531
+ DBINT data_len = dbdatlen(rwrap->client, col);
532
+ int null_val = ((data == NULL) && (data_len == 0));
533
+ if (!null_val)
534
+ identity = LL2NUM(*(DBBIGINT *)data);
535
+ }
536
+ }
537
+ return identity;
538
+ } else {
539
+ return Qnil;
540
+ }
541
+ }
542
+
543
+
544
+ // Lib Init
545
+
546
+ void init_tinytds_result() {
547
+ /* Data Classes */
548
+ cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
549
+ cDate = rb_const_get(rb_cObject, rb_intern("Date"));
550
+ cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
551
+ /* Define TinyTds::Result */
552
+ cTinyTdsResult = rb_define_class_under(mTinyTds, "Result", rb_cObject);
553
+ /* Define TinyTds::Result Public Methods */
554
+ rb_define_method(cTinyTdsResult, "fields", rb_tinytds_result_fields, 0);
555
+ rb_define_method(cTinyTdsResult, "each", rb_tinytds_result_each, -1);
556
+ rb_define_method(cTinyTdsResult, "cancel", rb_tinytds_result_cancel, 0);
557
+ rb_define_method(cTinyTdsResult, "do", rb_tinytds_result_do, 0);
558
+ rb_define_method(cTinyTdsResult, "affected_rows", rb_tinytds_result_affected_rows, 0);
559
+ rb_define_method(cTinyTdsResult, "return_code", rb_tinytds_result_return_code, 0);
560
+ rb_define_method(cTinyTdsResult, "insert", rb_tinytds_result_insert, 0);
561
+ /* Intern String Helpers */
562
+ intern_new = rb_intern("new");
563
+ intern_utc = rb_intern("utc");
564
+ intern_local = rb_intern("local");
565
+ intern_merge = rb_intern("merge");
566
+ intern_localtime = rb_intern("localtime");
567
+ intern_civil = rb_intern("civil");
568
+ intern_new_offset = rb_intern("new_offset");
569
+ intern_plus = rb_intern("+");
570
+ intern_divide = rb_intern("/");
571
+ intern_Rational = rb_intern("Rational");
572
+ /* Symbol Helpers */
573
+ sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
574
+ sym_as = ID2SYM(rb_intern("as"));
575
+ sym_array = ID2SYM(rb_intern("array"));
576
+ sym_cache_rows = ID2SYM(rb_intern("cache_rows"));
577
+ sym_first = ID2SYM(rb_intern("first"));
578
+ sym_local = ID2SYM(intern_local);
579
+ sym_utc = ID2SYM(intern_utc);
580
+ sym_timezone = ID2SYM(rb_intern("timezone"));
581
+ sym_empty_sets = ID2SYM(rb_intern("empty_sets"));
582
+ /* Data Conversion Options */
583
+ opt_decimal_zero = rb_str_new2("0.0");
584
+ rb_global_variable(&opt_decimal_zero);
585
+ opt_float_zero = rb_float_new((double)0);
586
+ rb_global_variable(&opt_float_zero);
587
+ opt_one = INT2NUM(1);
588
+ opt_zero = INT2NUM(0);
589
+ opt_four = INT2NUM(4);
590
+ opt_19hdr = INT2NUM(1900);
591
+ opt_tenk = INT2NUM(10000);
592
+ opt_onemil = INT2NUM(1000000);
593
+ /* Ruby version flags */
594
+ opt_ruby_186 = (rb_eval_string("RUBY_VERSION == '1.8.6'") == Qtrue) ? 1 : 0;
595
+ /* Encoding */
596
+ #ifdef HAVE_RUBY_ENCODING_H
597
+ binaryEncoding = rb_enc_find("binary");
598
+ #endif
599
+ }