#include "result.h" #include "datetime.h" #include <math.h> #define date_parse(klass, data,len) rb_funcall(datetime_parse(klass, data, len), fto_date, 0) VALUE cBigDecimal, cStringIO, cSwiftResult; ID fnew, fload, fto_date; void result_mark(ResultWrapper *handle) { if (handle) rb_gc_mark(handle->adapter); } void result_free(ResultWrapper *handle) { if (handle) { if (handle->free) delete handle->result; delete handle; } } VALUE result_alloc(VALUE klass) { ResultWrapper *handle = 0; return Data_Wrap_Struct(klass, result_mark, result_free, handle); } VALUE result_wrap_handle(VALUE klass, VALUE adapter, dbi::AbstractResult *result, bool free) { ResultWrapper *handle = new ResultWrapper; handle->result = result; handle->adapter = adapter; handle->free = free; VALUE obj = Data_Wrap_Struct(klass, result_mark, result_free, handle); if (!NIL_P(adapter)) rb_iv_set(obj, "@timezone", rb_iv_get(adapter, "@timezone")); return obj; } dbi::AbstractResult* result_handle(VALUE self) { ResultWrapper *handle; Data_Get_Struct(self, ResultWrapper, handle); if (!handle) rb_raise(eSwiftRuntimeError, "Invalid object, did you forget to call #super?"); return handle->result; } // NOTE clone and dup cannot be allowed since the underlying c++ object needs to be cloned, which // frankly is too much work :) static VALUE result_clone(VALUE self) { rb_raise(eSwiftRuntimeError, "clone is not allowed."); } 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::AbstractResult *result = result_handle(self); VALUE scheme = rb_iv_get(self, "@scheme"); try { std::vector<string> result_fields = result->fields(); std::vector<int> result_types = result->types(); std::vector<VALUE> fields; for (uint32_t i = 0; i < result_fields.size(); i++) fields.push_back(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, fields[column], typecast_field(result_types[column], data, length) ); } else { rb_hash_aset(tuple, fields[column], Qnil); } } // column loop NIL_P(scheme) ? rb_yield(tuple) : rb_yield(rb_funcall(scheme, fload, 1, tuple)); } // row loop } CATCH_DBI_EXCEPTIONS(); return Qnil; } VALUE typecast_field(int type, const char *data, uint64_t length) { switch(type) { case DBI_TYPE_BOOLEAN: return (data && (data[0] =='t' || data[0] == '1')) ? 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_TIMESTAMP: return datetime_parse(cSwiftDateTime, data, length); case DBI_TYPE_DATE: return date_parse(cSwiftDateTime, 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)); // DBI_TYPE_TIME // DBI_TYPE_TEXT default: return rb_enc_str_new(data, length, rb_utf8_encoding()); } } VALUE result_insert_id(VALUE self) { dbi::AbstractResult *result = result_handle(self); try { return SIZET2NUM(result->lastInsertID()); } CATCH_DBI_EXCEPTIONS(); return Qnil; } VALUE result_rows(VALUE self) { dbi::AbstractResult *result = result_handle(self); try { return SIZET2NUM(result->rows()); } CATCH_DBI_EXCEPTIONS(); } VALUE result_columns(VALUE self) { dbi::AbstractResult *result = result_handle(self); try { return SIZET2NUM(result->columns()); } CATCH_DBI_EXCEPTIONS(); } VALUE result_fields(VALUE self) { dbi::AbstractResult *result = result_handle(self); try { std::vector<string> result_fields = result->fields(); VALUE fields = rb_ary_new(); for (int i = 0; i < result_fields.size(); i++) rb_ary_push(fields, ID2SYM(rb_intern(result_fields[i].c_str()))); return fields; } CATCH_DBI_EXCEPTIONS(); } VALUE result_field_types(VALUE self) { dbi::AbstractResult *result = result_handle(self); std::vector<int> result_types = result->types(); VALUE types = rb_ary_new(); for (std::vector<int>::iterator it = result_types.begin(); it != result_types.end(); it++) { switch(*it) { case DBI_TYPE_BOOLEAN: rb_ary_push(types, rb_str_new2("boolean")); break; case DBI_TYPE_INT: rb_ary_push(types, rb_str_new2("integer")); break; case DBI_TYPE_BLOB: rb_ary_push(types, rb_str_new2("blob")); break; case DBI_TYPE_TIMESTAMP: rb_ary_push(types, rb_str_new2("timestamp")); break; case DBI_TYPE_DATE: rb_ary_push(types, rb_str_new2("date")); break; case DBI_TYPE_NUMERIC: rb_ary_push(types, rb_str_new2("numeric")); break; case DBI_TYPE_FLOAT: rb_ary_push(types, rb_str_new2("float")); break; case DBI_TYPE_TIME: rb_ary_push(types, rb_str_new2("time")); break; default: rb_ary_push(types, rb_str_new2("text")); } } return types; } VALUE result_retrieve(VALUE self) { dbi::AbstractResult *result = result_handle(self); while (result->consumeResult()); result->prepareResult(); return true; } void init_swift_result() { rb_require("bigdecimal"); rb_require("stringio"); VALUE mSwift = rb_define_module("Swift"); cSwiftResult = rb_define_class_under(mSwift, "Result", rb_cObject); cStringIO = CONST_GET(rb_mKernel, "StringIO"); cBigDecimal = CONST_GET(rb_mKernel, "BigDecimal"); fto_date = rb_intern("to_date"); fnew = rb_intern("new"); fload = rb_intern("load"); rb_define_alloc_func(cSwiftResult, result_alloc); rb_include_module(cSwiftResult, CONST_GET(rb_mKernel, "Enumerable")); rb_define_method(cSwiftResult, "retrieve", RUBY_METHOD_FUNC(result_retrieve), 0); 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, "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); rb_define_method(cSwiftResult, "field_types", RUBY_METHOD_FUNC(result_field_types), 0); }