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);