#include "result.h" VALUE cSwiftResult; VALUE cDateTime; VALUE cStringIO; VALUE cBigDecimal; VALUE fNew, fNewBang; uint64_t epoch_ajd_n, epoch_ajd_d; VALUE daysecs, sg; void result_free(dbi::AbstractResultSet *result) { if (result) { result->cleanup(); delete result; } } VALUE result_alloc(VALUE klass) { dbi::AbstractResultSet *result = 0; return Data_Wrap_Struct(klass, 0, result_free, result); } // TODO: static VALUE result_clone(VALUE self) { rb_raise(eSwiftRuntimeError, "clone is not allowed."); } // TODO: static VALUE result_dup(VALUE self) { rb_raise(eSwiftRuntimeError, "dup is not allowed."); } VALUE result_each(VALUE self) { uint64_t length; const char *data; dbi::AbstractResultSet *result = result_handle(self); VALUE scheme = rb_iv_get(self, "@scheme"); try { VALUE fields = rb_ary_new(); std::vector result_fields = result->fields(); std::vector result_types = result->types(); for (uint32_t i = 0; i < result_fields.size(); i++) { rb_ary_push(fields, ID2SYM(rb_intern(result_fields[i].c_str()))); } result->seek(0); for (uint32_t row = 0; row < result->rows(); row++) { VALUE tuple = rb_hash_new(); for (uint32_t column = 0; column < result->columns(); column++) { data = (const char*)result->read(row, column, &length); if (data) { rb_hash_aset( tuple, rb_ary_entry(fields, column), typecast_field(result_types[column], data, length) ); } else { rb_hash_aset(tuple, rb_ary_entry(fields, column), Qnil); } } // column loop NIL_P(scheme) ? rb_yield(tuple) : rb_yield(rb_funcall(scheme, rb_intern("load"), 1, tuple)); } // row loop } CATCH_DBI_EXCEPTIONS(); return Qnil; } dbi::AbstractResultSet* result_handle(VALUE self) { dbi::AbstractResultSet *result; Data_Get_Struct(self, dbi::AbstractResultSet, result); if (!result) rb_raise(eSwiftRuntimeError, "Invalid object, did you forget to call #super?"); return result; } static VALUE result_finish(VALUE self) { dbi::AbstractResultSet *result = result_handle(self); try { result->finish(); } CATCH_DBI_EXCEPTIONS(); } // Calculates local offset at a given time, including dst. size_t client_tzoffset(uint64_t local, int isdst) { struct tm tm; gmtime_r((const time_t*)&local, &tm); return local + (isdst ? 3600 : 0) - mktime(&tm); } // pinched from do_postgres static void reduce(uint64_t *numerator, uint64_t *denominator) { uint64_t a, b, c; a = *numerator; b = *denominator; while (a) { c = a; a = b % a; b = c; } *numerator = *numerator / b; *denominator = *denominator / b; } VALUE typecast_datetime(const char *data, uint64_t len) { struct tm tm; uint64_t epoch, adjust, offset; double usec = 0; char tzsign = 0; int tzhour = 0, tzmin = 0; memset(&tm, 0, sizeof(struct tm)); if (strchr(data, '.')) { sscanf(data, "%04d-%02d-%02d %02d:%02d:%02d%lf%c%02d:%02d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &usec, &tzsign, &tzhour, &tzmin); } else { sscanf(data, "%04d-%02d-%02d %02d:%02d:%02d%c%02d:%02d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &tzsign, &tzhour, &tzmin); } tm.tm_year -= 1900; tm.tm_mon -= 1; tm.tm_isdst = -1; if (tm.tm_mday > 0) { epoch = mktime(&tm); adjust = client_tzoffset(epoch, tm.tm_isdst); offset = adjust; if (tzsign == '+' || tzsign == '-') { offset = tzsign == '+' ? (time_t)tzhour * 3600 + (time_t)tzmin * 60 : (time_t)tzhour * -3600 + (time_t)tzmin * -60; } // 32bit platforms are for weenies uint64_t ajd_n = (epoch + adjust - offset), ajd_d = 86400L; reduce(&ajd_n, &ajd_d); ajd_n = epoch_ajd_n*ajd_d + ajd_n*epoch_ajd_d; ajd_d = epoch_ajd_d*ajd_d; reduce(&ajd_n, &ajd_d); VALUE ajd = rb_rational_new(SIZET2NUM(ajd_n), SIZET2NUM(ajd_d)); return rb_funcall(cDateTime, fNewBang, 3, ajd, rb_rational_new(INT2FIX(offset), daysecs), sg); } // TODO: throw a warning ? return rb_str_new(data, len); } VALUE typecast_field(int type, const char *data, uint64_t length) { switch(type) { case DBI_TYPE_BOOLEAN: return strcmp(data, "t") == 0 || strcmp(data, "1") == 0 ? Qtrue : Qfalse; case DBI_TYPE_INT: return rb_cstr2inum(data, 10); case DBI_TYPE_BLOB: return rb_funcall(cStringIO, fNew, 1, rb_str_new(data, length)); case DBI_TYPE_TEXT: return rb_enc_str_new(data, length, rb_utf8_encoding()); case DBI_TYPE_TIME: return typecast_datetime(data, length); case DBI_TYPE_NUMERIC: return rb_funcall(cBigDecimal, fNew, 1, rb_str_new2(data)); case DBI_TYPE_FLOAT: return rb_float_new(atof(data)); } } VALUE result_insert_id(VALUE self) { dbi::AbstractResultSet *result = result_handle(self); try { return SIZET2NUM(result->lastInsertID()); } CATCH_DBI_EXCEPTIONS(); return Qnil; } VALUE result_rows(VALUE self) { dbi::AbstractResultSet *result = result_handle(self); try { return SIZET2NUM(result->rows()); } CATCH_DBI_EXCEPTIONS(); } VALUE result_columns(VALUE self) { dbi::AbstractResultSet *result = result_handle(self); try { return SIZET2NUM(result->columns()); } CATCH_DBI_EXCEPTIONS(); } VALUE result_fields(VALUE self) { dbi::AbstractResultSet *result = result_handle(self); try { std::vector result_fields = result->fields(); VALUE fields = rb_ary_new(); for (int i = 0; i < result_fields.size(); i++) rb_ary_push(fields, rb_str_new2(result_fields[i].c_str())); return fields; } CATCH_DBI_EXCEPTIONS(); } void init_swift_result() { rb_require("bigdecimal"); rb_require("stringio"); rb_require("date"); VALUE mSwift = rb_define_module("Swift"); cSwiftResult = rb_define_class_under(mSwift, "Result", rb_cObject); cDateTime = CONST_GET(rb_mKernel, "DateTime"); cStringIO = CONST_GET(rb_mKernel, "StringIO"); cBigDecimal = CONST_GET(rb_mKernel, "BigDecimal"); fNew = rb_intern("new"); fNewBang = rb_intern("new!"); rb_define_alloc_func(cSwiftResult, result_alloc); rb_include_module(cSwiftResult, CONST_GET(rb_mKernel, "Enumerable")); rb_define_method(cSwiftResult, "clone", RUBY_METHOD_FUNC(result_clone), 0); rb_define_method(cSwiftResult, "dup", RUBY_METHOD_FUNC(result_dup), 0); rb_define_method(cSwiftResult, "each", RUBY_METHOD_FUNC(result_each), 0); rb_define_method(cSwiftResult, "finish", RUBY_METHOD_FUNC(result_finish), 0); rb_define_method(cSwiftResult, "insert_id", RUBY_METHOD_FUNC(result_insert_id), 0); rb_define_method(cSwiftResult, "rows", RUBY_METHOD_FUNC(result_rows), 0); rb_define_method(cSwiftResult, "columns", RUBY_METHOD_FUNC(result_columns), 0); rb_define_method(cSwiftResult, "fields", RUBY_METHOD_FUNC(result_fields), 0); // typecast_datetime setup. epoch_ajd_d = 2; epoch_ajd_n = 4881175L; // 1970-01-01 00:00:00 is 2440587.5 in ajd daysecs = SIZET2NUM(86400L); sg = SIZET2NUM(2299161L); // day of calendar reform Date::ITALY }