tiny_tds 0.6.2-x64-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,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
+ }