ext/tiny_tds/result.c in tiny_tds-0.3.2 vs ext/tiny_tds/result.c in tiny_tds-0.4.0
- old
+ new
@@ -8,10 +8,13 @@
int opt_ruby_186;
static ID intern_new, intern_utc, intern_local, intern_localtime, intern_merge,
intern_civil, intern_new_offset, intern_plus, intern_divide, intern_Rational;
static ID sym_symbolize_keys, sym_as, sym_array, sym_cache_rows, sym_first, sym_timezone, sym_local, sym_utc;
+
+// Lib Macros
+
#ifdef HAVE_RUBY_ENCODING_H
rb_encoding *binaryEncoding;
#define ENCODED_STR_NEW(_data, _len) ({ \
VALUE _val = rb_str_new((char *)_data, (long)_len); \
rb_enc_associate(_val, rwrap->encoding); \
@@ -35,11 +38,13 @@
static void rb_tinytds_result_mark(void *ptr) {
tinytds_result_wrapper *rwrap = (tinytds_result_wrapper *)ptr;
if (rwrap) {
rb_gc_mark(rwrap->local_offset);
rb_gc_mark(rwrap->fields);
+ rb_gc_mark(rwrap->fields_processed);
rb_gc_mark(rwrap->results);
+ rb_gc_mark(rwrap->dbresults_retcodes);
}
}
static void rb_tinytds_result_free(void *ptr) {
tinytds_result_wrapper *rwrap = (tinytds_result_wrapper *)ptr;
@@ -50,22 +55,56 @@
VALUE obj;
tinytds_result_wrapper *rwrap;
obj = Data_Make_Struct(cTinyTdsResult, tinytds_result_wrapper, rb_tinytds_result_mark, rb_tinytds_result_free, rwrap);
rwrap->client = c;
rwrap->local_offset = Qnil;
- rwrap->fields = Qnil;
+ rwrap->fields = rb_ary_new();
+ rwrap->fields_processed = rb_ary_new();
rwrap->results = Qnil;
+ rwrap->dbresults_retcodes = rb_ary_new();
rwrap->number_of_results = 0;
rwrap->number_of_fields = 0;
rwrap->number_of_rows = 0;
rb_obj_call_init(obj, 0, NULL);
return obj;
}
// Lib Backend (Helpers)
+static RETCODE rb_tinytds_result_dbresults_retcode(VALUE self) {
+ GET_RESULT_WRAPPER(self);
+ VALUE ruby_rc;
+ RETCODE db_rc;
+ ruby_rc = rb_ary_entry(rwrap->dbresults_retcodes, rwrap->number_of_results);
+ if (NIL_P(ruby_rc)) {
+ db_rc = dbresults(rwrap->client);
+ ruby_rc = INT2FIX(db_rc);
+ rb_ary_store(rwrap->dbresults_retcodes, rwrap->number_of_results, ruby_rc);
+ } else {
+ db_rc = FIX2INT(ruby_rc);
+ }
+ return db_rc;
+}
+
+static RETCODE rb_tinytds_result_ok_helper(DBPROCESS *client) {
+ GET_CLIENT_USERDATA(client);
+ if (userdata->dbsqlok_sent == 0) {
+ userdata->dbsqlok_retcode = dbsqlok(client);
+ userdata->dbsqlok_sent = 1;
+ }
+ return userdata->dbsqlok_retcode;
+}
+
+static void rb_tinytds_result_cancel_helper(DBPROCESS *client) {
+ GET_CLIENT_USERDATA(client);
+ rb_tinytds_result_ok_helper(client);
+ dbcancel(client);
+ userdata->dbcancel_sent = 1;
+ userdata->dbsql_sent = 0;
+}
+
static VALUE rb_tinytds_result_fetch_row(VALUE self, ID timezone, int symbolize_keys, int as_array) {
/* Wrapper And Local Vars */
GET_RESULT_WRAPPER(self);
/* Create Empty Row */
VALUE row = as_array ? rb_ary_new2(rwrap->number_of_fields) : rb_hash_new();
@@ -150,17 +189,17 @@
data_len = sizeof(new_data);
}
case SYBDATETIME: {
DBDATEREC date_rec;
dbdatecrack(rwrap->client, &date_rec, (DBDATETIME *)data);
- int year = date_rec.year,
- month = date_rec.month,
- day = date_rec.day,
- hour = date_rec.hour,
- min = date_rec.minute,
- sec = date_rec.second,
- msec = date_rec.millisecond;
+ int year = date_rec.dateyear,
+ month = date_rec.datemonth+1,
+ day = date_rec.datedmonth,
+ hour = date_rec.datehour,
+ min = date_rec.dateminute,
+ sec = date_rec.datesecond,
+ msec = date_rec.datemsecond;
if (year+month+day+hour+min+sec+msec != 0) {
VALUE offset = (timezone == intern_local) ? rwrap->local_offset : opt_zero;
/* Use DateTime */
if (year < 1902 || year+month+day > 2058) {
VALUE datetime_sec = INT2NUM(sec);
@@ -208,133 +247,149 @@
}
// TinyTds::Client (public)
+static VALUE rb_tinytds_result_fields(VALUE self) {
+ GET_RESULT_WRAPPER(self);
+ RETCODE dbsqlok_rc = rb_tinytds_result_ok_helper(rwrap->client);
+ RETCODE dbresults_rc = rb_tinytds_result_dbresults_retcode(self);
+ VALUE fields_processed = rb_ary_entry(rwrap->fields_processed, rwrap->number_of_results);
+ if ((dbsqlok_rc == SUCCEED) && (dbresults_rc == SUCCEED) && (fields_processed == Qnil)) {
+ /* Default query options. */
+ VALUE qopts = rb_iv_get(self, "@query_options");
+ int symbolize_keys = (rb_hash_aref(qopts, sym_symbolize_keys) == Qtrue) ? 1 : 0;
+ /* Set number_of_fields count for this result set. */
+ rwrap->number_of_fields = dbnumcols(rwrap->client);
+ if (rwrap->number_of_fields > 0) {
+ /* Create fields for this result set. */
+ unsigned int fldi = 0;
+ VALUE fields = rb_ary_new2(rwrap->number_of_fields);
+ for (fldi = 0; fldi < rwrap->number_of_fields; fldi++) {
+ char *colname = dbcolname(rwrap->client, fldi+1);
+ VALUE field = symbolize_keys ? ID2SYM(rb_intern(colname)) : rb_obj_freeze(ENCODED_STR_NEW2(colname));
+ rb_ary_store(fields, fldi, field);
+ }
+ /* Store the fields. */
+ if (rwrap->number_of_results == 0) {
+ rwrap->fields = fields;
+ } else if (rwrap->number_of_results == 1) {
+ VALUE multi_rs_fields = rb_ary_new();
+ rb_ary_store(multi_rs_fields, 0, rwrap->fields);
+ rb_ary_store(multi_rs_fields, 1, fields);
+ rwrap->fields = multi_rs_fields;
+ } else {
+ rb_ary_store(rwrap->fields, rwrap->number_of_results, fields);
+ }
+ }
+ rb_ary_store(rwrap->fields_processed, rwrap->number_of_results, Qtrue);
+ }
+ return rwrap->fields;
+}
+
static VALUE rb_tinytds_result_each(int argc, VALUE * argv, VALUE self) {
GET_RESULT_WRAPPER(self);
+ GET_CLIENT_USERDATA(rwrap->client);
/* Local Vars */
- VALUE defaults, opts, block;
+ VALUE qopts, opts, block;
ID timezone;
int symbolize_keys = 0, as_array = 0, cache_rows = 0, first = 0;
- /* Merge Options Hash, Populate Opts & Block Var */
- defaults = rb_iv_get(self, "@query_options");
- if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1) {
- opts = rb_funcall(defaults, intern_merge, 1, opts);
- } else {
- opts = defaults;
- }
+ /* Merge Options Hash To Query Options. Populate Opts & Block Var. */
+ qopts = rb_iv_get(self, "@query_options");
+ if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1)
+ qopts = rb_funcall(qopts, intern_merge, 1, opts);
+ rb_iv_set(self, "@query_options", qopts);
/* Locals From Options */
- if (rb_hash_aref(opts, sym_first) == Qtrue)
+ if (rb_hash_aref(qopts, sym_first) == Qtrue)
first = 1;
- if (rb_hash_aref(opts, sym_symbolize_keys) == Qtrue)
+ if (rb_hash_aref(qopts, sym_symbolize_keys) == Qtrue)
symbolize_keys = 1;
- if (rb_hash_aref(opts, sym_as) == sym_array)
+ if (rb_hash_aref(qopts, sym_as) == sym_array)
as_array = 1;
- if (rb_hash_aref(opts, sym_cache_rows) == Qtrue)
+ if (rb_hash_aref(qopts, sym_cache_rows) == Qtrue)
cache_rows = 1;
- if (rb_hash_aref(opts, sym_timezone) == sym_local) {
+ if (rb_hash_aref(qopts, sym_timezone) == sym_local) {
timezone = intern_local;
- } else if (rb_hash_aref(opts, sym_timezone) == sym_utc) {
+ } else if (rb_hash_aref(qopts, sym_timezone) == sym_utc) {
timezone = intern_utc;
} else {
rb_warn(":timezone option must be :utc or :local - defaulting to :local");
timezone = intern_local;
}
/* Make The Results Or Yield Existing */
if (NIL_P(rwrap->results)) {
rwrap->results = rb_ary_new();
- RETCODE dbsqlok_rc = 0;
- RETCODE dbresults_rc = 0;
- dbsqlok_rc = dbsqlok(rwrap->client);
- dbresults_rc = dbresults(rwrap->client);
+ RETCODE dbsqlok_rc = rb_tinytds_result_ok_helper(rwrap->client);
+ RETCODE dbresults_rc = rb_tinytds_result_dbresults_retcode(self);
while ((dbsqlok_rc == SUCCEED) && (dbresults_rc == SUCCEED)) {
- /* Only do field and row work if there are rows in this result set. */
int has_rows = (DBROWS(rwrap->client) == SUCCEED) ? 1 : 0;
- int number_of_fields = has_rows ? dbnumcols(rwrap->client) : 0;
- if (has_rows && (number_of_fields > 0)) {
- /* Create fields for this result set. */
- unsigned int fldi = 0;
- rwrap->number_of_fields = number_of_fields;
- VALUE fields = rb_ary_new2(rwrap->number_of_fields);
- for (fldi = 0; fldi < rwrap->number_of_fields; fldi++) {
- char *colname = dbcolname(rwrap->client, fldi+1);
- VALUE field = symbolize_keys ? ID2SYM(rb_intern(colname)) : rb_obj_freeze(ENCODED_STR_NEW2(colname));
- rb_ary_store(fields, fldi, field);
- }
- /* Store the fields. */
- if (rwrap->number_of_results == 0) {
- rwrap->fields = fields;
- } else if (rwrap->number_of_results == 1) {
- VALUE multi_rs_fields = rb_ary_new();
- rb_ary_store(multi_rs_fields, 0, rwrap->fields);
- rb_ary_store(multi_rs_fields, 1, fields);
- rwrap->fields = multi_rs_fields;
- } else {
- rb_ary_store(rwrap->fields, rwrap->number_of_results, fields);
- }
+ rb_tinytds_result_fields(self);
+ if (has_rows && rwrap->number_of_fields > 0) {
/* Create rows for this result set. */
unsigned long rowi = 0;
VALUE result = rb_ary_new();
while (dbnextrow(rwrap->client) != NO_MORE_ROWS) {
VALUE row = rb_tinytds_result_fetch_row(self, timezone, symbolize_keys, as_array);
if (cache_rows)
rb_ary_store(result, rowi, row);
if (!NIL_P(block))
rb_yield(row);
- if (first)
+ if (first) {
dbcanquery(rwrap->client);
+ userdata->dbcancel_sent = 1;
+ }
rowi++;
}
rwrap->number_of_rows = rowi;
/* Store the result. */
- if (rwrap->number_of_results == 0) {
- rwrap->results = result;
- } else if (rwrap->number_of_results == 1) {
- VALUE multi_resultsets = rb_ary_new();
- rb_ary_store(multi_resultsets, 0, rwrap->results);
- rb_ary_store(multi_resultsets, 1, result);
- rwrap->results = multi_resultsets;
- } else {
- rb_ary_store(rwrap->results, rwrap->number_of_results, result);
+ if (cache_rows) {
+ if (rwrap->number_of_results == 0) {
+ rwrap->results = result;
+ } else if (rwrap->number_of_results == 1) {
+ VALUE multi_resultsets = rb_ary_new();
+ rb_ary_store(multi_resultsets, 0, rwrap->results);
+ rb_ary_store(multi_resultsets, 1, result);
+ rwrap->results = multi_resultsets;
+ } else {
+ rb_ary_store(rwrap->results, rwrap->number_of_results, result);
+ }
}
- /* Record the result set */
+ // If we find results increment the counter that helpers use and setup the next loop.
rwrap->number_of_results = rwrap->number_of_results + 1;
+ dbresults_rc = rb_tinytds_result_dbresults_retcode(self);
+ } else {
+ // If we do not find results, side step the rb_tinytds_result_dbresults_retcode helper and
+ // manually populate its memoized array while nullifing any memoized fields too before loop.
+ dbresults_rc = dbresults(rwrap->client);
+ rb_ary_store(rwrap->dbresults_retcodes, rwrap->number_of_results, INT2FIX(dbresults_rc));
+ rb_ary_store(rwrap->fields_processed, rwrap->number_of_results, Qnil);
}
- dbresults_rc = dbresults(rwrap->client);
}
- if (dbresults_rc == FAIL) {
- // TODO: Account for something in the dbresults() while loop set the return code to FAIL.
- rb_warn("TinyTds: Something in the dbresults() while loop set the return code to FAIL.\n");
- }
+ if (dbresults_rc == FAIL)
+ rb_warn("TinyTDS: Something in the dbresults() while loop set the return code to FAIL.\n");
+ userdata->dbsql_sent = 0;
} else if (!NIL_P(block)) {
unsigned long i;
for (i = 0; i < rwrap->number_of_rows; i++) {
rb_yield(rb_ary_entry(rwrap->results, i));
}
}
return rwrap->results;
}
-static VALUE rb_tinytds_result_fields(VALUE self) {
- GET_RESULT_WRAPPER(self);
- return rwrap->fields;
-}
-
static VALUE rb_tinytds_result_cancel(VALUE self) {
GET_RESULT_WRAPPER(self);
- if (rwrap->client)
- dbsqlok(rwrap->client);
- dbcancel(rwrap->client);
+ GET_CLIENT_USERDATA(rwrap->client);
+ if (rwrap->client && !userdata->dbcancel_sent)
+ rb_tinytds_result_cancel_helper(rwrap->client);
return Qtrue;
}
static VALUE rb_tinytds_result_do(VALUE self) {
GET_RESULT_WRAPPER(self);
if (rwrap->client) {
- dbsqlok(rwrap->client);
- dbcancel(rwrap->client);
+ rb_tinytds_result_cancel_helper(rwrap->client);
return LONG2NUM((long)dbcount(rwrap->client));
} else {
return Qnil;
}
}
@@ -359,12 +414,11 @@
}
static VALUE rb_tinytds_result_insert(VALUE self) {
GET_RESULT_WRAPPER(self);
if (rwrap->client) {
- dbsqlok(rwrap->client);
- dbcancel(rwrap->client);
+ rb_tinytds_result_cancel_helper(rwrap->client);
VALUE identity = Qnil;
dbcmd(rwrap->client, "SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident");
if (dbsqlexec(rwrap->client) != FAIL && dbresults(rwrap->client) != FAIL && DBROWS(rwrap->client) != FAIL) {
while (dbnextrow(rwrap->client) != NO_MORE_ROWS) {
int col = 1;
@@ -390,11 +444,11 @@
cDate = rb_const_get(rb_cObject, rb_intern("Date"));
cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
/* Define TinyTds::Result */
cTinyTdsResult = rb_define_class_under(mTinyTds, "Result", rb_cObject);
/* Define TinyTds::Result Public Methods */
- rb_define_method(cTinyTdsResult, "each", rb_tinytds_result_each, -1);
rb_define_method(cTinyTdsResult, "fields", rb_tinytds_result_fields, 0);
+ rb_define_method(cTinyTdsResult, "each", rb_tinytds_result_each, -1);
rb_define_method(cTinyTdsResult, "cancel", rb_tinytds_result_cancel, 0);
rb_define_method(cTinyTdsResult, "do", rb_tinytds_result_do, 0);
rb_define_method(cTinyTdsResult, "affected_rows", rb_tinytds_result_affected_rows, 0);
rb_define_method(cTinyTdsResult, "return_code", rb_tinytds_result_return_code, 0);
rb_define_method(cTinyTdsResult, "insert", rb_tinytds_result_insert, 0);